1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-29 02:11:15 +02:00

Refactor settings processing

Put all Nix configuration flags in a Settings object.
This commit is contained in:
Eelco Dolstra 2012-07-30 19:55:41 -04:00
parent d50d7a2874
commit 97421eb5ec
19 changed files with 596 additions and 552 deletions

View file

@ -229,8 +229,6 @@ private:
public:
bool cacheFailure;
/* Set if at least one derivation had a BuildError (i.e. permanent
failure). */
bool permanentFailure;
@ -314,7 +312,7 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
if (result == ecNoSubstituters) ++nrNoSubstituters;
if (waitees.empty() || (result == ecFailed && !keepGoing)) {
if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) {
/* If we failed and keepGoing is not set, we remove all
remaining waitees. */
@ -466,14 +464,13 @@ void UserLock::acquire()
{
assert(uid == 0);
string buildUsersGroup = querySetting("build-users-group", "");
assert(buildUsersGroup != "");
assert(settings.buildUsersGroup != "");
/* Get the members of the build-users-group. */
struct group * gr = getgrnam(buildUsersGroup.c_str());
struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
if (!gr)
throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
% buildUsersGroup);
% settings.buildUsersGroup);
gid = gr->gr_gid;
/* Copy the result of getgrnam. */
@ -485,7 +482,7 @@ void UserLock::acquire()
if (users.empty())
throw Error(format("the build users group `%1%' has no members")
% buildUsersGroup);
% settings.buildUsersGroup);
/* Find a user account that isn't currently in use for another
build. */
@ -495,11 +492,11 @@ void UserLock::acquire()
struct passwd * pw = getpwnam(i->c_str());
if (!pw)
throw Error(format("the user `%1%' in the group `%2%' does not exist")
% *i % buildUsersGroup);
% *i % settings.buildUsersGroup);
createDirs(nixStateDir + "/userpool");
createDirs(settings.nixStateDir + "/userpool");
fnUserLock = (format("%1%/userpool/%2%") % nixStateDir % pw->pw_uid).str();
fnUserLock = (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid).str();
if (lockedPaths.find(fnUserLock) != lockedPaths.end())
/* We already have a lock on this one. */
@ -519,7 +516,7 @@ void UserLock::acquire()
/* Sanity check... */
if (uid == getuid() || uid == geteuid())
throw Error(format("the Nix user should not be a member of `%1%'")
% buildUsersGroup);
% settings.buildUsersGroup);
return;
}
@ -527,7 +524,7 @@ void UserLock::acquire()
throw Error(format("all build users are currently in use; "
"consider creating additional users and adding them to the `%1%' group")
% buildUsersGroup);
% settings.buildUsersGroup);
}
@ -546,7 +543,7 @@ static void runSetuidHelper(const string & command,
const string & arg)
{
Path program = getEnv("NIX_SETUID_HELPER",
nixLibexecDir + "/nix-setuid-helper");
settings.nixLibexecDir + "/nix-setuid-helper");
/* Fork. */
Pid pid;
@ -601,12 +598,6 @@ bool amPrivileged()
}
bool haveBuildUsers()
{
return querySetting("build-users-group", "") != "";
}
void getOwnership(const Path & path)
{
runSetuidHelper("get-ownership", path);
@ -622,7 +613,7 @@ void deletePathWrapped(const Path & path,
} catch (SysError & e) {
/* If this failed due to a permission error, then try it with
the setuid helper. */
if (haveBuildUsers() && !amPrivileged()) {
if (settings.buildUsersGroup != "" && !amPrivileged()) {
getOwnership(path);
deletePath(path, bytesFreed, blocksFreed);
} else
@ -701,9 +692,9 @@ HookInstance::HookInstance()
throw SysError("dupping builder's stdout/stderr");
/* XXX: Pass `buildTimeout' to the hook? */
execl(buildHook.c_str(), buildHook.c_str(), thisSystem.c_str(),
(format("%1%") % maxSilentTime).str().c_str(),
(format("%1%") % printBuildTrace).str().c_str(),
execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
(format("%1%") % settings.maxSilentTime).str().c_str(),
(format("%1%") % settings.printBuildTrace).str().c_str(),
NULL);
throw SysError(format("executing `%1%'") % buildHook);
@ -943,7 +934,7 @@ void DerivationGoal::init()
{
trace("init");
if (readOnlyMode)
if (settings.readOnlyMode)
throw Error(format("cannot build derivation `%1%' - no write access to the Nix store") % drvPath);
/* The first thing to do is to make sure that the derivation
@ -995,7 +986,7 @@ void DerivationGoal::haveDerivation()
/* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build
them. */
if (queryBoolSetting("build-use-substitutes", true))
if (settings.useSubstitutes)
foreach (PathSet::iterator, i, invalidOutputs)
addWaitee(worker.makeSubstitutionGoal(*i));
@ -1010,7 +1001,7 @@ void DerivationGoal::outputsSubstituted()
{
trace("all outputs substituted (maybe)");
if (nrFailed > 0 && nrFailed > nrNoSubstituters && !tryFallback)
if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback)
throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath);
nrFailed = nrNoSubstituters = 0;
@ -1109,9 +1100,9 @@ PathSet outputPaths(const DerivationOutputs & outputs)
static bool canBuildLocally(const string & platform)
{
return platform == thisSystem
return platform == settings.thisSystem
#ifdef CAN_DO_LINUX32_BUILDS
|| (platform == "i686-linux" && thisSystem == "x86_64-linux")
|| (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
#endif
;
}
@ -1213,7 +1204,7 @@ void DerivationGoal::tryToBuild()
derivation prefers to be done locally, do it even if
maxBuildJobs is 0. */
unsigned int curBuilds = worker.getNrLocalBuilds();
if (curBuilds >= maxBuildJobs && !(preferLocalBuild && curBuilds == 0)) {
if (curBuilds >= settings.maxBuildJobs && !(preferLocalBuild && curBuilds == 0)) {
worker.waitForBuildSlot(shared_from_this());
outputLocks.unlock();
return;
@ -1228,7 +1219,7 @@ void DerivationGoal::tryToBuild()
printMsg(lvlError, e.msg());
outputLocks.unlock();
buildUser.release();
if (printBuildTrace)
if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%")
% drvPath % drv.outputs["out"].path % 0 % e.msg());
worker.permanentFailure = true;
@ -1360,7 +1351,7 @@ void DerivationGoal::buildDone()
bool hookError = hook &&
(!WIFEXITED(status) || WEXITSTATUS(status) != 100);
if (printBuildTrace) {
if (settings.printBuildTrace) {
if (hook && hookError)
printMsg(lvlError, format("@ hook-failed %1% %2% %3% %4%")
% drvPath % drv.outputs["out"].path % status % e.msg());
@ -1376,7 +1367,7 @@ void DerivationGoal::buildDone()
able to access the network). Hook errors (like
communication problems with the remote machine) shouldn't
be cached either. */
if (worker.cacheFailure && !hookError && !fixedOutput)
if (settings.cacheFailure && !hookError && !fixedOutput)
foreach (DerivationOutputs::iterator, i, drv.outputs)
worker.store.registerFailedPath(i->second.path);
@ -1388,7 +1379,7 @@ void DerivationGoal::buildDone()
/* Release the build user, if applicable. */
buildUser.release();
if (printBuildTrace) {
if (settings.printBuildTrace) {
printMsg(lvlError, format("@ build-succeeded %1% %2%")
% drvPath % drv.outputs["out"].path);
}
@ -1399,7 +1390,7 @@ void DerivationGoal::buildDone()
HookReply DerivationGoal::tryBuildHook()
{
if (!useBuildHook || getEnv("NIX_BUILD_HOOK") == "") return rpDecline;
if (!settings.useBuildHook || getEnv("NIX_BUILD_HOOK") == "") return rpDecline;
if (!worker.hook)
worker.hook = boost::shared_ptr<HookInstance>(new HookInstance);
@ -1412,7 +1403,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Send the request to the hook. */
writeLine(worker.hook->toHook.writeSide, (format("%1% %2% %3% %4%")
% (worker.getNrLocalBuilds() < maxBuildJobs ? "1" : "0")
% (worker.getNrLocalBuilds() < settings.maxBuildJobs ? "1" : "0")
% drv.platform % drvPath % concatStringsSep(",", features)).str());
/* Read the first line of input, which should be a word indicating
@ -1471,7 +1462,7 @@ HookReply DerivationGoal::tryBuildHook()
fds.insert(hook->builderOut.readSide);
worker.childStarted(shared_from_this(), hook->pid, fds, false, false);
if (printBuildTrace)
if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-started %1% %2% %3% %4%")
% drvPath % drv.outputs["out"].path % drv.platform % logFile);
@ -1502,7 +1493,7 @@ void DerivationGoal::startBuilder()
if (!canBuildLocally(drv.platform))
throw Error(
format("a `%1%' is required to build `%3%', but I am a `%2%'")
% drv.platform % thisSystem % drvPath);
% drv.platform % settings.thisSystem % drvPath);
/* Construct the environment passed to the builder. */
@ -1523,10 +1514,10 @@ void DerivationGoal::startBuilder()
shouldn't care, but this is useful for purity checking (e.g.,
the compiler or linker might only want to accept paths to files
in the store or in the build directory). */
env["NIX_STORE"] = nixStore;
env["NIX_STORE"] = settings.nixStore;
/* The maximum number of cores to utilize for parallel building. */
env["NIX_BUILD_CORES"] = (format("%d") % buildCores).str();
env["NIX_BUILD_CORES"] = (format("%d") % settings.buildCores).str();
/* Add all bindings specified in the derivation. */
foreach (StringPairs::iterator, i, drv.env)
@ -1618,7 +1609,7 @@ void DerivationGoal::startBuilder()
/* If `build-users-group' is not empty, then we have to build as
one of the members of that group. */
if (haveBuildUsers()) {
if (settings.buildUsersGroup != "") {
buildUser.acquire();
assert(buildUser.getUID() != 0);
assert(buildUser.getGID() != 0);
@ -1640,15 +1631,15 @@ void DerivationGoal::startBuilder()
the builder can create its output but not mess with the
outputs of other processes). */
struct stat st;
if (stat(nixStore.c_str(), &st) == -1)
throw SysError(format("cannot stat `%1%'") % nixStore);
if (stat(settings.nixStore.c_str(), &st) == -1)
throw SysError(format("cannot stat `%1%'") % settings.nixStore);
if (!(st.st_mode & S_ISVTX) ||
((st.st_mode & S_IRWXG) != S_IRWXG) ||
(st.st_gid != buildUser.getGID()))
throw Error(format(
"builder does not have write permission to `%2%'; "
"try `chgrp %1% %2%; chmod 1775 %2%'")
% buildUser.getGID() % nixStore);
% buildUser.getGID() % settings.nixStore);
}
@ -1657,7 +1648,7 @@ void DerivationGoal::startBuilder()
functions like fetchurl (which needs a proper /etc/resolv.conf)
work properly. Purity checking for fixed-output derivations
is somewhat pointless anyway. */
useChroot = queryBoolSetting("build-use-chroot", false);
useChroot = settings.useChroot;
if (fixedOutput) useChroot = false;
@ -1707,16 +1698,8 @@ void DerivationGoal::startBuilder()
writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
/* Bind-mount a user-configurable set of directories from the
host file system. The `/dev/pts' directory must be mounted
separately so that newly-created pseudo-terminals show
up. */
Paths defaultDirs;
defaultDirs.push_back("/dev");
defaultDirs.push_back("/dev/pts");
Paths dirsInChroot_ = querySetting("build-chroot-dirs", defaultDirs);
dirsInChroot.insert(dirsInChroot_.begin(), dirsInChroot_.end());
host file system. */
dirsInChroot = settings.dirsInChroot;
dirsInChroot.insert(tmpDir);
/* Make the closure of the inputs available in the chroot,
@ -1726,8 +1709,8 @@ void DerivationGoal::startBuilder()
can be bind-mounted). !!! As an extra security
precaution, make the fake Nix store only writable by the
build user. */
createDirs(chrootRootDir + nixStore);
chmod(chrootRootDir + nixStore, 01777);
createDirs(chrootRootDir + settings.nixStore);
chmod(chrootRootDir + settings.nixStore, 01777);
foreach (PathSet::iterator, i, inputPaths) {
struct stat st;
@ -1827,7 +1810,7 @@ void DerivationGoal::startBuilder()
worker.childStarted(shared_from_this(), pid,
singleton<set<int> >(builderOut.readSide), true, true);
if (printBuildTrace) {
if (settings.printBuildTrace) {
printMsg(lvlError, format("@ build-started %1% %2% %3% %4%")
% drvPath % drv.outputs["out"].path % drv.platform % logFile);
}
@ -1907,16 +1890,14 @@ void DerivationGoal::initChild()
#ifdef CAN_DO_LINUX32_BUILDS
/* Change the personality to 32-bit if we're doing an
i686-linux build on an x86_64-linux machine. */
if (drv.platform == "i686-linux" && thisSystem == "x86_64-linux") {
if (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux") {
if (personality(0x0008 | 0x8000000 /* == PER_LINUX32_3GB */) == -1)
throw SysError("cannot set i686-linux personality");
}
/* Impersonate a Linux 2.6 machine to get some determinism in
builds that depend on the kernel version. */
if ((drv.platform == "i686-linux" || drv.platform == "x86_64-linux") &&
queryBoolSetting("build-impersonate-linux-26", true))
{
if ((drv.platform == "i686-linux" || drv.platform == "x86_64-linux") && settings.impersonateLinux26) {
int cur = personality(0xffffffff);
if (cur != -1) personality(cur | 0x0020000 /* == UNAME26 */);
}
@ -1958,7 +1939,7 @@ void DerivationGoal::initChild()
} else {
/* Let the setuid helper take care of it. */
program = nixLibexecDir + "/nix-setuid-helper";
program = settings.nixLibexecDir + "/nix-setuid-helper";
args.push_back(program.c_str());
args.push_back("run-builder");
user = buildUser.getUser().c_str();
@ -2126,13 +2107,13 @@ string drvsLogDir = "drvs";
Path DerivationGoal::openLogFile()
{
if (!queryBoolSetting("build-keep-log", true)) return "";
if (!settings.keepLog) return "";
/* Create a log file. */
Path dir = (format("%1%/%2%") % nixLogDir % drvsLogDir).str();
Path dir = (format("%1%/%2%") % settings.nixLogDir % drvsLogDir).str();
createDirs(dir);
if (queryBoolSetting("build-compress-log", true)) {
if (settings.compressLog) {
Path logFileName = (format("%1%/%2%.bz2") % dir % baseNameOf(drvPath)).str();
AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
@ -2179,7 +2160,7 @@ void DerivationGoal::closeLogFile()
void DerivationGoal::deleteTmpDir(bool force)
{
if (tmpDir != "") {
if (keepFailed && !force) {
if (settings.keepFailed && !force) {
printMsg(lvlError,
format("builder for `%1%' failed; keeping build directory `%2%'")
% drvPath % tmpDir);
@ -2199,7 +2180,7 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
if ((hook && fd == hook->builderOut.readSide) ||
(!hook && fd == builderOut.readSide))
{
if (verbosity >= buildVerbosity)
if (verbosity >= settings.buildVerbosity)
writeToStderr((unsigned char *) data.data(), data.size());
if (bzLogFile) {
int err;
@ -2235,13 +2216,13 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid)
bool DerivationGoal::pathFailed(const Path & path)
{
if (!worker.cacheFailure) return false;
if (!settings.cacheFailure) return false;
if (!worker.store.hasPathFailed(path)) return false;
printMsg(lvlError, format("builder for `%1%' failed previously (cached)") % path);
if (printBuildTrace)
if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-failed %1% %2% cached") % drvPath % path);
worker.permanentFailure = true;
@ -2362,10 +2343,10 @@ void SubstitutionGoal::init()
return;
}
if (readOnlyMode)
if (settings.readOnlyMode)
throw Error(format("cannot substitute path `%1%' - no write access to the Nix store") % storePath);
subs = substituters;
subs = settings.substituters;
tryNext();
}
@ -2437,7 +2418,7 @@ void SubstitutionGoal::tryToRun()
is maxBuildJobs == 0 (no local builds allowed), we still allow
a substituter to run. This is because substitutions cannot be
distributed to another machine via the build hook. */
if (worker.getNrLocalBuilds() >= (maxBuildJobs == 0 ? 1 : maxBuildJobs)) {
if (worker.getNrLocalBuilds() >= (settings.maxBuildJobs == 0 ? 1 : settings.maxBuildJobs)) {
worker.waitForBuildSlot(shared_from_this());
return;
}
@ -2496,7 +2477,7 @@ void SubstitutionGoal::tryToRun()
/* Pass configuration options (including those overriden
with --option) to the substituter. */
setenv("_NIX_OPTIONS", packSettings().c_str(), 1);
setenv("_NIX_OPTIONS", settings.pack().c_str(), 1);
/* Fill in the arguments. */
Strings args;
@ -2525,7 +2506,7 @@ void SubstitutionGoal::tryToRun()
state = &SubstitutionGoal::finished;
if (printBuildTrace) {
if (settings.printBuildTrace) {
printMsg(lvlError, format("@ substituter-started %1% %2%")
% storePath % sub);
}
@ -2583,7 +2564,7 @@ void SubstitutionGoal::finished()
printMsg(lvlInfo, e.msg());
if (printBuildTrace) {
if (settings.printBuildTrace) {
printMsg(lvlError, format("@ substituter-failed %1% %2% %3%")
% storePath % status % e.msg());
}
@ -2611,7 +2592,7 @@ void SubstitutionGoal::finished()
printMsg(lvlChatty,
format("substitution of path `%1%' succeeded") % storePath);
if (printBuildTrace) {
if (settings.printBuildTrace) {
printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath);
}
@ -2622,7 +2603,7 @@ void SubstitutionGoal::finished()
void SubstitutionGoal::handleChildOutput(int fd, const string & data)
{
assert(fd == logPipe.readSide);
if (verbosity >= buildVerbosity)
if (verbosity >= settings.buildVerbosity)
writeToStderr((unsigned char *) data.data(), data.size());
/* Don't write substitution output to a log file for now. We
probably should, though. */
@ -2650,7 +2631,6 @@ Worker::Worker(LocalStore & store)
working = true;
nrLocalBuilds = 0;
lastWokenUp = 0;
cacheFailure = queryBoolSetting("build-cache-failure", false);
permanentFailure = false;
}
@ -2715,7 +2695,7 @@ void Worker::removeGoal(GoalPtr goal)
topGoals.erase(goal);
/* If a top-level goal failed, then kill all other goals
(unless keepGoing was set). */
if (goal->getExitCode() == Goal::ecFailed && !keepGoing)
if (goal->getExitCode() == Goal::ecFailed && !settings.keepGoing)
topGoals.clear();
}
@ -2787,7 +2767,7 @@ void Worker::childTerminated(pid_t pid, bool wakeSleepers)
void Worker::waitForBuildSlot(GoalPtr goal)
{
debug("wait for build slot");
if (getNrLocalBuilds() < maxBuildJobs)
if (getNrLocalBuilds() < settings.maxBuildJobs)
wakeUp(goal); /* we can do it right away */
else
wantingToBuild.insert(goal);
@ -2836,7 +2816,7 @@ void Worker::run(const Goals & _topGoals)
if (!children.empty() || !waitingForAWhile.empty())
waitForInput();
else {
if (awake.empty() && maxBuildJobs == 0) throw Error(
if (awake.empty() && settings.maxBuildJobs == 0) throw Error(
"unable to start any build; either increase `--max-jobs' "
"or enable distributed builds");
assert(!awake.empty());
@ -2846,9 +2826,9 @@ void Worker::run(const Goals & _topGoals)
/* If --keep-going is not set, it's possible that the main goal
exited while some of its subgoals were still active. But if
--keep-going *is* set, then they must all be finished now. */
assert(!keepGoing || awake.empty());
assert(!keepGoing || wantingToBuild.empty());
assert(!keepGoing || children.empty());
assert(!settings.keepGoing || awake.empty());
assert(!settings.keepGoing || wantingToBuild.empty());
assert(!settings.keepGoing || children.empty());
}
@ -2868,15 +2848,15 @@ void Worker::waitForInput()
time_t before = time(0);
/* If a global timeout has been set, sleep until it's done. */
if (buildTimeout != 0) {
if (settings.buildTimeout != 0) {
useTimeout = true;
if (lastWait == 0 || lastWait > before) lastWait = before;
timeout.tv_sec = std::max((time_t) 0, lastWait + buildTimeout - before);
timeout.tv_sec = std::max((time_t) 0, lastWait + settings.buildTimeout - before);
}
/* If we're monitoring for silence on stdout/stderr, sleep until
the first deadline for any child. */
if (maxSilentTime != 0) {
if (settings.maxSilentTime != 0) {
time_t oldest = 0;
foreach (Children::iterator, i, children) {
if (i->second.monitorForSilence) {
@ -2885,7 +2865,7 @@ void Worker::waitForInput()
}
}
if (oldest) {
time_t silenceTimeout = std::max((time_t) 0, oldest + maxSilentTime - before);
time_t silenceTimeout = std::max((time_t) 0, oldest + settings.maxSilentTime - before);
timeout.tv_sec = useTimeout
? std::min(silenceTimeout, timeout.tv_sec)
: silenceTimeout;
@ -2896,14 +2876,12 @@ void Worker::waitForInput()
/* If we are polling goals that are waiting for a lock, then wake
up after a few seconds at most. */
int wakeUpInterval = queryIntSetting("build-poll-interval", 5);
if (!waitingForAWhile.empty()) {
useTimeout = true;
if (lastWokenUp == 0)
printMsg(lvlError, "waiting for locks or build slots...");
if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before;
timeout.tv_sec = std::max((time_t) 0, lastWokenUp + wakeUpInterval - before);
timeout.tv_sec = std::max((time_t) 0, lastWokenUp + settings.pollInterval - before);
} else lastWokenUp = 0;
using namespace std;
@ -2969,27 +2947,27 @@ void Worker::waitForInput()
}
}
if (maxSilentTime != 0 &&
if (settings.maxSilentTime != 0 &&
j->second.monitorForSilence &&
after - j->second.lastOutput >= (time_t) maxSilentTime)
after - j->second.lastOutput >= (time_t) settings.maxSilentTime)
{
printMsg(lvlError,
format("%1% timed out after %2% seconds of silence")
% goal->getName() % maxSilentTime);
% goal->getName() % settings.maxSilentTime);
goal->cancel();
}
if (buildTimeout != 0 &&
after - before >= (time_t) buildTimeout)
if (settings.buildTimeout != 0 &&
after - before >= (time_t) settings.buildTimeout)
{
printMsg(lvlError,
format("%1% timed out after %2% seconds of activity")
% goal->getName() % buildTimeout);
% goal->getName() % settings.buildTimeout);
goal->cancel();
}
}
if (!waitingForAWhile.empty() && lastWokenUp + wakeUpInterval <= after) {
if (!waitingForAWhile.empty() && lastWokenUp + settings.pollInterval <= after) {
lastWokenUp = after;
foreach (WeakGoals::iterator, i, waitingForAWhile) {
GoalPtr goal = i->lock();