mirror of
https://github.com/NixOS/nix.git
synced 2025-11-15 15:02:42 +01:00
Merge pull request #13860 from obsidiansystems/derivation-building-resources-code-cleanup
Derivation building resources code cleanup
This commit is contained in:
commit
de7f137f31
5 changed files with 127 additions and 136 deletions
|
|
@ -54,24 +54,9 @@ DerivationBuildingGoal::~DerivationBuildingGoal()
|
||||||
{
|
{
|
||||||
/* 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
|
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
|
||||||
if (builder) {
|
if (builder)
|
||||||
try {
|
builder.reset();
|
||||||
builder->stopDaemon();
|
|
||||||
} catch (...) {
|
|
||||||
ignoreExceptionInDestructor();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
builder->deleteTmpDir(false);
|
|
||||||
} catch (...) {
|
|
||||||
ignoreExceptionInDestructor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
try {
|
try {
|
||||||
closeLogFile();
|
closeLogFile();
|
||||||
|
|
@ -95,22 +80,8 @@ void DerivationBuildingGoal::killChild()
|
||||||
hook.reset();
|
hook.reset();
|
||||||
#endif
|
#endif
|
||||||
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
|
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
|
||||||
if (builder && builder->pid != -1) {
|
if (builder && builder->killChild())
|
||||||
worker.childTerminated(this);
|
worker.childTerminated(this);
|
||||||
|
|
||||||
// FIXME: move this into DerivationBuilder.
|
|
||||||
|
|
||||||
/* 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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -653,11 +624,6 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
goal.worker.childTerminated(&goal);
|
goal.worker.childTerminated(&goal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void markContentsGood(const StorePath & path) override
|
|
||||||
{
|
|
||||||
goal.worker.markContentsGood(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path openLogFile() override
|
Path openLogFile() override
|
||||||
{
|
{
|
||||||
return goal.openLogFile();
|
return goal.openLogFile();
|
||||||
|
|
@ -756,21 +722,22 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
|
|
||||||
trace("build done");
|
trace("build done");
|
||||||
|
|
||||||
auto res = builder->unprepareBuild();
|
SingleDrvOutputs builtOutputs;
|
||||||
// N.B. cannot use `std::visit` with co-routine return
|
try {
|
||||||
if (auto * ste = std::get_if<0>(&res)) {
|
builtOutputs = builder->unprepareBuild();
|
||||||
|
} catch (BuildError & e) {
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
// Allow selecting a subset of enum values
|
// Allow selecting a subset of enum values
|
||||||
# pragma GCC diagnostic push
|
# pragma GCC diagnostic push
|
||||||
# pragma GCC diagnostic ignored "-Wswitch-enum"
|
# pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||||
switch (ste->status) {
|
switch (e.status) {
|
||||||
case BuildResult::HashMismatch:
|
case BuildResult::HashMismatch:
|
||||||
worker.hashMismatch = true;
|
worker.hashMismatch = true;
|
||||||
/* See header, the protocols don't know about `HashMismatch`
|
/* See header, the protocols don't know about `HashMismatch`
|
||||||
yet, so change it to `OutputRejected`, which they expect
|
yet, so change it to `OutputRejected`, which they expect
|
||||||
for this case (hash mismatch is a type of output
|
for this case (hash mismatch is a type of output
|
||||||
rejection). */
|
rejection). */
|
||||||
ste->status = BuildResult::OutputRejected;
|
e.status = BuildResult::OutputRejected;
|
||||||
break;
|
break;
|
||||||
case BuildResult::NotDeterministic:
|
case BuildResult::NotDeterministic:
|
||||||
worker.checkMismatch = true;
|
worker.checkMismatch = true;
|
||||||
|
|
@ -780,11 +747,15 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
# pragma GCC diagnostic pop
|
# pragma GCC diagnostic pop
|
||||||
co_return doneFailure(std::move(*ste));
|
co_return doneFailure(std::move(e));
|
||||||
} else if (auto * builtOutputs = std::get_if<1>(&res)) {
|
}
|
||||||
|
{
|
||||||
StorePathSet outputPaths;
|
StorePathSet outputPaths;
|
||||||
for (auto & [_, output] : *builtOutputs)
|
for (auto & [_, output] : builtOutputs) {
|
||||||
|
// for sake of `bmRepair`
|
||||||
|
worker.markContentsGood(output.outPath);
|
||||||
outputPaths.insert(output.outPath);
|
outputPaths.insert(output.outPath);
|
||||||
|
}
|
||||||
runPostBuildHook(worker.store, *logger, drvPath, outputPaths);
|
runPostBuildHook(worker.store, *logger, drvPath, outputPaths);
|
||||||
|
|
||||||
/* It is now safe to delete the lock files, since all future
|
/* It is now safe to delete the lock files, since all future
|
||||||
|
|
@ -793,9 +764,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
(unlinked) lock files. */
|
(unlinked) lock files. */
|
||||||
outputLocks.setDeletion(true);
|
outputLocks.setDeletion(true);
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
co_return doneSuccess(BuildResult::Built, std::move(*builtOutputs));
|
co_return doneSuccess(BuildResult::Built, std::move(builtOutputs));
|
||||||
} else {
|
|
||||||
unreachable();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,6 @@ struct DerivationBuilderCallbacks
|
||||||
* @todo this should be reworked
|
* @todo this should be reworked
|
||||||
*/
|
*/
|
||||||
virtual void childTerminated() = 0;
|
virtual void childTerminated() = 0;
|
||||||
|
|
||||||
virtual void markContentsGood(const StorePath & path) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -124,11 +122,6 @@ struct DerivationBuilderCallbacks
|
||||||
*/
|
*/
|
||||||
struct DerivationBuilder : RestrictionContext
|
struct DerivationBuilder : RestrictionContext
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* The process ID of the builder.
|
|
||||||
*/
|
|
||||||
Pid pid;
|
|
||||||
|
|
||||||
DerivationBuilder() = default;
|
DerivationBuilder() = default;
|
||||||
virtual ~DerivationBuilder() = default;
|
virtual ~DerivationBuilder() = default;
|
||||||
|
|
||||||
|
|
@ -161,25 +154,18 @@ struct DerivationBuilder : RestrictionContext
|
||||||
* processing. A status code and exception are returned, providing
|
* processing. A status code and exception are returned, providing
|
||||||
* more information. The second case indicates success, and
|
* more information. The second case indicates success, and
|
||||||
* realisations for each output of the derivation are returned.
|
* realisations for each output of the derivation are returned.
|
||||||
|
*
|
||||||
|
* @throws BuildError
|
||||||
*/
|
*/
|
||||||
virtual std::variant<BuildError, SingleDrvOutputs> unprepareBuild() = 0;
|
virtual SingleDrvOutputs unprepareBuild() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the in-process nix daemon thread.
|
* Forcibly kill the child process, if any.
|
||||||
* @see startDaemon
|
*
|
||||||
|
* @returns whether the child was still alive and needed to be
|
||||||
|
* killed.
|
||||||
*/
|
*/
|
||||||
virtual void stopDaemon() = 0;
|
virtual bool killChild() = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the temporary directory, if we have one.
|
|
||||||
*/
|
|
||||||
virtual void deleteTmpDir(bool force) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kill any processes running under the build user UID or in the
|
|
||||||
* cgroup of the build.
|
|
||||||
*/
|
|
||||||
virtual void killSandbox(bool getStats) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
|
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,6 @@ struct ChrootDerivationBuilder : virtual DerivationBuilderImpl
|
||||||
|
|
||||||
PathsInChroot pathsInChroot;
|
PathsInChroot pathsInChroot;
|
||||||
|
|
||||||
void deleteTmpDir(bool force) override
|
|
||||||
{
|
|
||||||
autoDelChroot.reset(); /* this runs the destructor */
|
|
||||||
|
|
||||||
DerivationBuilderImpl::deleteTmpDir(force);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool needsHashRewrite() override
|
bool needsHashRewrite() override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -166,13 +159,13 @@ struct ChrootDerivationBuilder : virtual DerivationBuilderImpl
|
||||||
return !needsHashRewrite() ? chrootRootDir + p : store.toRealPath(p);
|
return !needsHashRewrite() ? chrootRootDir + p : store.toRealPath(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanupBuild() override
|
void cleanupBuild(bool force) override
|
||||||
{
|
{
|
||||||
DerivationBuilderImpl::cleanupBuild();
|
DerivationBuilderImpl::cleanupBuild(force);
|
||||||
|
|
||||||
/* Move paths out of the chroot for easier debugging of
|
/* Move paths out of the chroot for easier debugging of
|
||||||
build failures. */
|
build failures. */
|
||||||
if (buildMode == bmNormal)
|
if (!force && buildMode == bmNormal)
|
||||||
for (auto & [_, status] : initialOutputs) {
|
for (auto & [_, status] : initialOutputs) {
|
||||||
if (!status.known)
|
if (!status.known)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -182,6 +175,8 @@ struct ChrootDerivationBuilder : virtual DerivationBuilderImpl
|
||||||
if (pathExists(chrootRootDir + p))
|
if (pathExists(chrootRootDir + p))
|
||||||
std::filesystem::rename((chrootRootDir + p), p);
|
std::filesystem::rename((chrootRootDir + p), p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
autoDelChroot.reset(); /* this runs the destructor */
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Path, Path> addDependencyPrep(const StorePath & path)
|
std::pair<Path, Path> addDependencyPrep(const StorePath & path)
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,11 @@ class DerivationBuilderImpl : public DerivationBuilder, public DerivationBuilder
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The process ID of the builder.
|
||||||
|
*/
|
||||||
|
Pid pid;
|
||||||
|
|
||||||
LocalStore & store;
|
LocalStore & store;
|
||||||
|
|
||||||
std::unique_ptr<DerivationBuilderCallbacks> miscMethods;
|
std::unique_ptr<DerivationBuilderCallbacks> miscMethods;
|
||||||
|
|
@ -86,6 +91,27 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~DerivationBuilderImpl()
|
||||||
|
{
|
||||||
|
/* Careful: we should never ever throw an exception from a
|
||||||
|
destructor. */
|
||||||
|
try {
|
||||||
|
killChild();
|
||||||
|
} catch (...) {
|
||||||
|
ignoreExceptionInDestructor();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
stopDaemon();
|
||||||
|
} catch (...) {
|
||||||
|
ignoreExceptionInDestructor();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cleanupBuild(false);
|
||||||
|
} catch (...) {
|
||||||
|
ignoreExceptionInDestructor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -192,7 +218,7 @@ public:
|
||||||
|
|
||||||
void startBuilder() override;
|
void startBuilder() override;
|
||||||
|
|
||||||
std::variant<BuildError, SingleDrvOutputs> unprepareBuild() override;
|
SingleDrvOutputs unprepareBuild() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
@ -286,9 +312,11 @@ private:
|
||||||
*/
|
*/
|
||||||
void startDaemon();
|
void startDaemon();
|
||||||
|
|
||||||
public:
|
/**
|
||||||
|
* Stop the in-process nix daemon thread.
|
||||||
void stopDaemon() override;
|
* @see startDaemon
|
||||||
|
*/
|
||||||
|
void stopDaemon();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
@ -343,15 +371,25 @@ private:
|
||||||
*/
|
*/
|
||||||
SingleDrvOutputs registerOutputs();
|
SingleDrvOutputs registerOutputs();
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
void deleteTmpDir(bool force) override;
|
|
||||||
|
|
||||||
void killSandbox(bool getStats) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void cleanupBuild();
|
/**
|
||||||
|
* Delete the temporary directory, if we have one.
|
||||||
|
*
|
||||||
|
* @param force We know the build suceeded, so don't attempt to
|
||||||
|
* preseve anything for debugging.
|
||||||
|
*/
|
||||||
|
virtual void cleanupBuild(bool force);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kill any processes running under the build user UID or in the
|
||||||
|
* cgroup of the build.
|
||||||
|
*/
|
||||||
|
virtual void killSandbox(bool getStats);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool killChild() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
@ -414,6 +452,24 @@ void DerivationBuilderImpl::killSandbox(bool getStats)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DerivationBuilderImpl::killChild()
|
||||||
|
{
|
||||||
|
bool ret = pid != -1;
|
||||||
|
if (ret) {
|
||||||
|
/* 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(-pid, SIGKILL); /* ignore the result */
|
||||||
|
|
||||||
|
killSandbox(true);
|
||||||
|
|
||||||
|
pid.wait();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool DerivationBuilderImpl::prepareBuild()
|
bool DerivationBuilderImpl::prepareBuild()
|
||||||
{
|
{
|
||||||
if (useBuildUsers()) {
|
if (useBuildUsers()) {
|
||||||
|
|
@ -427,7 +483,7 @@ bool DerivationBuilderImpl::prepareBuild()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<BuildError, SingleDrvOutputs> DerivationBuilderImpl::unprepareBuild()
|
SingleDrvOutputs DerivationBuilderImpl::unprepareBuild()
|
||||||
{
|
{
|
||||||
// FIXME: get rid of this, rely on RAII.
|
// FIXME: get rid of this, rely on RAII.
|
||||||
Finally releaseBuildUser([&]() {
|
Finally releaseBuildUser([&]() {
|
||||||
|
|
@ -476,56 +532,42 @@ std::variant<BuildError, SingleDrvOutputs> DerivationBuilderImpl::unprepareBuild
|
||||||
((double) buildResult.cpuSystem->count()) / 1000000);
|
((double) buildResult.cpuSystem->count()) / 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool diskFull = false;
|
/* Check the exit status. */
|
||||||
|
if (!statusOk(status)) {
|
||||||
|
|
||||||
try {
|
bool diskFull = decideWhetherDiskFull();
|
||||||
|
|
||||||
/* Check the exit status. */
|
cleanupBuild(false);
|
||||||
if (!statusOk(status)) {
|
|
||||||
|
|
||||||
diskFull |= decideWhetherDiskFull();
|
auto msg =
|
||||||
|
fmt("Cannot build '%s'.\n"
|
||||||
|
"Reason: " ANSI_RED "builder %s" ANSI_NORMAL ".",
|
||||||
|
Magenta(store.printStorePath(drvPath)),
|
||||||
|
statusToString(status));
|
||||||
|
|
||||||
cleanupBuild();
|
msg += showKnownOutputs(store, drv);
|
||||||
|
|
||||||
auto msg =
|
miscMethods->appendLogTailErrorMsg(msg);
|
||||||
fmt("Cannot build '%s'.\n"
|
|
||||||
"Reason: " ANSI_RED "builder %s" ANSI_NORMAL ".",
|
|
||||||
Magenta(store.printStorePath(drvPath)),
|
|
||||||
statusToString(status));
|
|
||||||
|
|
||||||
msg += showKnownOutputs(store, drv);
|
if (diskFull)
|
||||||
|
msg += "\nnote: build failure may have been caused by lack of free disk space";
|
||||||
|
|
||||||
miscMethods->appendLogTailErrorMsg(msg);
|
throw BuildError(
|
||||||
|
!derivationType.isSandboxed() || diskFull ? BuildResult::TransientFailure : BuildResult::PermanentFailure,
|
||||||
if (diskFull)
|
msg);
|
||||||
msg += "\nnote: build failure may have been caused by lack of free disk space";
|
|
||||||
|
|
||||||
throw BuildError(
|
|
||||||
!derivationType.isSandboxed() || diskFull ? BuildResult::TransientFailure
|
|
||||||
: BuildResult::PermanentFailure,
|
|
||||||
msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute the FS closure of the outputs and register them as
|
|
||||||
being valid. */
|
|
||||||
auto builtOutputs = registerOutputs();
|
|
||||||
|
|
||||||
/* Delete unused redirected outputs (when doing hash rewriting). */
|
|
||||||
for (auto & i : redirectedOutputs)
|
|
||||||
deletePath(store.Store::toRealPath(i.second));
|
|
||||||
|
|
||||||
deleteTmpDir(true);
|
|
||||||
|
|
||||||
return std::move(builtOutputs);
|
|
||||||
|
|
||||||
} catch (BuildError & e) {
|
|
||||||
return std::move(e);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void DerivationBuilderImpl::cleanupBuild()
|
/* Compute the FS closure of the outputs and register them as
|
||||||
{
|
being valid. */
|
||||||
deleteTmpDir(false);
|
auto builtOutputs = registerOutputs();
|
||||||
|
|
||||||
|
/* Delete unused redirected outputs (when doing hash rewriting). */
|
||||||
|
for (auto & i : redirectedOutputs)
|
||||||
|
deletePath(store.Store::toRealPath(i.second));
|
||||||
|
|
||||||
|
cleanupBuild(true);
|
||||||
|
|
||||||
|
return builtOutputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chmod_(const Path & path, mode_t mode)
|
static void chmod_(const Path & path, mode_t mode)
|
||||||
|
|
@ -1757,7 +1799,6 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||||
}
|
}
|
||||||
|
|
||||||
store.optimisePath(actualPath, NoRepair); // FIXME: combine with scanForReferences()
|
store.optimisePath(actualPath, NoRepair); // FIXME: combine with scanForReferences()
|
||||||
miscMethods->markContentsGood(newInfo.path);
|
|
||||||
|
|
||||||
newInfo.deriver = drvPath;
|
newInfo.deriver = drvPath;
|
||||||
newInfo.ultimate = true;
|
newInfo.ultimate = true;
|
||||||
|
|
@ -1825,7 +1866,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||||
return builtOutputs;
|
return builtOutputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationBuilderImpl::deleteTmpDir(bool force)
|
void DerivationBuilderImpl::cleanupBuild(bool force)
|
||||||
{
|
{
|
||||||
if (topTmpDir != "") {
|
if (topTmpDir != "") {
|
||||||
/* As an extra precaution, even in the event of `deletePath` failing to
|
/* As an extra precaution, even in the event of `deletePath` failing to
|
||||||
|
|
|
||||||
|
|
@ -659,7 +659,7 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu
|
||||||
throw SysError("setuid failed");
|
throw SysError("setuid failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<BuildError, SingleDrvOutputs> unprepareBuild() override
|
SingleDrvOutputs unprepareBuild() override
|
||||||
{
|
{
|
||||||
sandboxMountNamespace = -1;
|
sandboxMountNamespace = -1;
|
||||||
sandboxUserNamespace = -1;
|
sandboxUserNamespace = -1;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue