From 97421eb5ecde86b75441094fda017b12b5eca2a6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 30 Jul 2012 19:55:41 -0400 Subject: [PATCH] Refactor settings processing Put all Nix configuration flags in a Settings object. --- perl/lib/Nix/Store.xs | 2 +- src/libexpr/eval.cc | 4 +- src/libexpr/primops.cc | 6 +- src/libmain/shared.cc | 47 +++-- src/libstore/build.cc | 176 +++++++--------- src/libstore/derivations.cc | 22 +- src/libstore/gc.cc | 78 +++---- src/libstore/globals.cc | 213 ++++++++++---------- src/libstore/globals.hh | 269 ++++++++++++++++--------- src/libstore/local-store.cc | 64 +++--- src/libstore/local-store.hh | 49 +++-- src/libstore/misc.cc | 24 +-- src/libstore/optimise-store.cc | 28 ++- src/libstore/remote-store.cc | 24 +-- src/libstore/store-api.cc | 16 +- src/nix-env/nix-env.cc | 20 +- src/nix-instantiate/nix-instantiate.cc | 4 +- src/nix-store/nix-store.cc | 70 +++---- src/nix-worker/nix-worker.cc | 32 ++- 19 files changed, 596 insertions(+), 552 deletions(-) diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index 76de674e6..00311aa8f 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -19,7 +19,7 @@ void doInit() { if (!store) { try { - setDefaultsFromEnvironment(); + settings.processEnvironment(); store = openStore(); } catch (Error & e) { croak(e.what()); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index cf7c62ad2..74f7560fe 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -179,7 +179,7 @@ EvalState::EvalState() searchPathInsertionPoint = searchPath.end(); Strings paths = tokenizeString(getEnv("NIX_PATH", ""), ":"); foreach (Strings::iterator, i, paths) addToSearchPath(*i); - addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs"); + addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs"); searchPathInsertionPoint = searchPath.begin(); createBaseEnv(); @@ -1058,7 +1058,7 @@ string EvalState::coerceToString(Value & v, PathSet & context, if (srcToStore[path] != "") dstPath = srcToStore[path]; else { - dstPath = readOnlyMode + dstPath = settings.readOnlyMode ? computeStorePathForPath(path).first : store->addToStore(path); srcToStore[path] = dstPath; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5c011c43e..7258c4cc0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -623,7 +623,7 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v) refs.insert(path); } - Path storePath = readOnlyMode + Path storePath = settings.readOnlyMode ? computeStorePathForText(name, contents, refs) : store->addTextToStore(name, contents, refs); @@ -687,7 +687,7 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v) FilterFromExpr filter(state, *args[0]); - Path dstPath = readOnlyMode + Path dstPath = settings.readOnlyMode ? computeStorePathForPath(path, true, htSHA256, filter).first : store->addToStore(path, true, htSHA256, filter); @@ -1079,7 +1079,7 @@ void EvalState::createBaseEnv() mkInt(v, time(0)); addConstant("__currentTime", v); - mkString(v, thisSystem.c_str()); + mkString(v, settings.thisSystem.c_str()); addConstant("__currentSystem", v); // Miscellaneous diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 2118e4391..9c62e320f 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -74,7 +74,7 @@ void printMissing(StoreAPI & store, const PathSet & paths) if (!unknown.empty()) { printMsg(lvlInfo, format("don't know how to build these paths%1%:") - % (readOnlyMode ? " (may be caused by read-only store access)" : "")); + % (settings.readOnlyMode ? " (may be caused by read-only store access)" : "")); foreach (PathSet::iterator, i, unknown) printMsg(lvlInfo, format(" %1%") % *i); } @@ -93,11 +93,20 @@ static void setLogType(string lt) static bool showTrace = false; +string getArg(const string & opt, + Strings::iterator & i, const Strings::iterator & end) +{ + ++i; + if (i == end) throw UsageError(format("`%1%' requires an argument") % opt); + return *i; +} + /* Initialize and reorder arguments, then call the actual argument processor. */ static void initAndRun(int argc, char * * argv) { - setDefaultsFromEnvironment(); + settings.processEnvironment(); + settings.loadConfFile(); /* Catch SIGINT. */ struct sigaction act; @@ -156,20 +165,19 @@ static void initAndRun(int argc, char * * argv) remaining.clear(); /* Process default options. */ - int verbosityDelta = 0; + int verbosityDelta = lvlInfo; for (Strings::iterator i = args.begin(); i != args.end(); ++i) { string arg = *i; if (arg == "--verbose" || arg == "-v") verbosityDelta++; else if (arg == "--quiet") verbosityDelta--; else if (arg == "--log-type") { - ++i; - if (i == args.end()) throw UsageError("`--log-type' requires an argument"); - setLogType(*i); + string s = getArg(arg, i, args.end()); + setLogType(s); } else if (arg == "--no-build-output" || arg == "-Q") - buildVerbosity = lvlVomit; + settings.buildVerbosity = lvlVomit; else if (arg == "--print-build-trace") - printBuildTrace = true; + settings.printBuildTrace = true; else if (arg == "--help") { printHelp(); return; @@ -179,23 +187,23 @@ static void initAndRun(int argc, char * * argv) return; } else if (arg == "--keep-failed" || arg == "-K") - keepFailed = true; + settings.keepFailed = true; else if (arg == "--keep-going" || arg == "-k") - keepGoing = true; + settings.keepGoing = true; else if (arg == "--fallback") - tryFallback = true; + settings.tryFallback = true; else if (arg == "--max-jobs" || arg == "-j") - maxBuildJobs = getIntArg(arg, i, args.end()); + settings.set("build-max-jobs", getArg(arg, i, args.end())); else if (arg == "--cores") - buildCores = getIntArg(arg, i, args.end()); + settings.set("build-cores", getArg(arg, i, args.end())); else if (arg == "--readonly-mode") - readOnlyMode = true; + settings.readOnlyMode = true; else if (arg == "--max-silent-time") - maxSilentTime = getIntArg(arg, i, args.end()); + settings.set("build-max-silent-time", getArg(arg, i, args.end())); else if (arg == "--timeout") - buildTimeout = getIntArg(arg, i, args.end()); + settings.set("build-timeout", getArg(arg, i, args.end())); else if (arg == "--no-build-hook") - useBuildHook = false; + settings.useBuildHook = false; else if (arg == "--show-trace") showTrace = true; else if (arg == "--option") { @@ -203,14 +211,15 @@ static void initAndRun(int argc, char * * argv) string name = *i; ++i; if (i == args.end()) throw UsageError("`--option' requires two arguments"); string value = *i; - overrideSetting(name, tokenizeString(value)); + settings.set(name, value); } else remaining.push_back(arg); } - verbosityDelta += queryIntSetting("verbosity", lvlInfo); verbosity = (Verbosity) (verbosityDelta < 0 ? 0 : verbosityDelta); + settings.update(); + run(remaining); /* Close the Nix database. */ diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 4a2bc5218..0972d6e19 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -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(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 >(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(); diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 97343d57d..73047c753 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -12,7 +12,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash { recursive = false; string algo = hashAlgo; - + if (string(algo, 0, 2) == "r:") { recursive = true; algo = string(algo, 2); @@ -21,7 +21,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash hashType = parseHashType(algo); if (hashType == htUnknown) throw Error(format("unknown hash algorithm `%1%'") % algo); - + hash = parseHash(hashType, this->hash); } @@ -38,7 +38,7 @@ Path writeDerivation(StoreAPI & store, held during a garbage collection). */ string suffix = name + drvExtension; string contents = unparseDerivation(drv); - return readOnlyMode + return settings.readOnlyMode ? computeStorePathForText(suffix, contents, references) : store.addTextToStore(suffix, contents, references); } @@ -51,7 +51,7 @@ static Path parsePath(std::istream & str) throw Error(format("bad path `%1%' in derivation") % s); return s; } - + static StringSet parseStrings(std::istream & str, bool arePaths) { @@ -60,7 +60,7 @@ static StringSet parseStrings(std::istream & str, bool arePaths) res.insert(arePaths ? parsePath(str) : parseString(str)); return res; } - + Derivation parseDerivation(const string & s) { @@ -106,7 +106,7 @@ Derivation parseDerivation(const string & s) expect(str, ")"); drv.env[name] = value; } - + expect(str, ")"); return drv; } @@ -165,7 +165,7 @@ string unparseDerivation(const Derivation & drv) s += "],"; printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end()); - + s += ','; printString(s, drv.platform); s += ','; printString(s, drv.builder); s += ','; printStrings(s, drv.args.begin(), drv.args.end()); @@ -178,9 +178,9 @@ string unparseDerivation(const Derivation & drv) s += ','; printString(s, i->second); s += ')'; } - + s += "])"; - + return s; } @@ -190,7 +190,7 @@ bool isDerivation(const string & fileName) return hasSuffix(fileName, drvExtension); } - + bool isFixedOutputDrv(const Derivation & drv) { return drv.outputs.size() == 1 && @@ -247,7 +247,7 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv) inputs2[printHash(h)] = i->second; } drv.inputDrvs = inputs2; - + return hashString(htSHA256, unparseDerivation(drv)); } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 874efe4d3..1355702f8 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -34,10 +34,10 @@ static const int defaultGcLevel = 1000; int LocalStore::openGCLock(LockType lockType) { Path fnGCLock = (format("%1%/%2%") - % nixStateDir % gcLockName).str(); - + % settings.nixStateDir % gcLockName).str(); + debug(format("acquiring global GC lock `%1%'") % fnGCLock); - + AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600); if (fdGCLock == -1) throw SysError(format("opening global GC lock `%1%'") % fnGCLock); @@ -51,7 +51,7 @@ int LocalStore::openGCLock(LockType lockType) /* !!! Restrict read permission on the GC root. Otherwise any process that can open the file for reading can DoS the collector. */ - + return fdGCLock.borrow(); } @@ -85,7 +85,7 @@ void LocalStore::addIndirectRoot(const Path & path) { string hash = printHash32(hashString(htSHA1, path)); Path realRoot = canonPath((format("%1%/%2%/auto/%3%") - % nixStateDir % gcRootsDir % hash).str()); + % settings.nixStateDir % gcRootsDir % hash).str()); createSymlink(realRoot, path); } @@ -113,15 +113,15 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, else { if (!allowOutsideRootsDir) { - Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str()); - + Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str()); + if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/") throw Error(format( "path `%1%' is not a valid garbage collector root; " "it's not in the directory `%2%'") % gcRoot % rootsDir); } - + createSymlink(gcRoot, storePath); } @@ -130,10 +130,10 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, Instead of reading all the roots, it would be more efficient to check if the root is in a directory in or linked from the gcroots directory. */ - if (queryBoolSetting("gc-check-reachability", false)) { + if (settings.checkRootReachability) { Roots roots = store.findRoots(); if (roots.find(gcRoot) == roots.end()) - printMsg(lvlError, + printMsg(lvlError, format( "warning: `%1%' is not in a directory where the garbage collector looks for roots; " "therefore, `%2%' might be removed by the garbage collector") @@ -144,7 +144,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, progress. This prevents the set of permanent roots from increasing while a GC is in progress. */ store.syncWithGC(); - + return gcRoot; } @@ -160,23 +160,23 @@ void LocalStore::addTempRoot(const Path & path) if (fdTempRoots == -1) { while (1) { - Path dir = (format("%1%/%2%") % nixStateDir % tempRootsDir).str(); + Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str(); createDirs(dir); - + fnTempRoots = (format("%1%/%2%") % dir % getpid()).str(); AutoCloseFD fdGCLock = openGCLock(ltRead); - + if (pathExists(fnTempRoots)) /* It *must* be stale, since there can be no two processes with the same pid. */ unlink(fnTempRoots.c_str()); - fdTempRoots = openLockFile(fnTempRoots, true); + fdTempRoots = openLockFile(fnTempRoots, true); fdGCLock.close(); - + debug(format("acquiring read lock on `%1%'") % fnTempRoots); lockFile(fdTempRoots, ltRead, true); @@ -186,7 +186,7 @@ void LocalStore::addTempRoot(const Path & path) if (fstat(fdTempRoots, &st) == -1) throw SysError(format("statting `%1%'") % fnTempRoots); if (st.st_size == 0) break; - + /* The garbage collector deleted this file before we could get a lock. (It won't delete the file after we get a lock.) Try again. */ @@ -218,7 +218,7 @@ void removeTempRoots() /* Automatically clean up the temporary roots file when we exit. */ -struct RemoveTempRoots +struct RemoveTempRoots { ~RemoveTempRoots() { @@ -238,10 +238,10 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) /* Read the `temproots' directory for per-process temporary root files. */ Strings tempRootFiles = readDirectory( - (format("%1%/%2%") % nixStateDir % tempRootsDir).str()); + (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str()); foreach (Strings::iterator, i, tempRootFiles) { - Path path = (format("%1%/%2%/%3%") % nixStateDir % tempRootsDir % *i).str(); + Path path = (format("%1%/%2%/%3%") % settings.nixStateDir % tempRootsDir % *i).str(); debug(format("reading temporary root file `%1%'") % path); FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666))); @@ -295,7 +295,7 @@ static void findRoots(StoreAPI & store, const Path & path, bool recurseSymlinks, bool deleteStale, Roots & roots) { try { - + struct stat st; if (lstat(path.c_str(), &st) == -1) throw SysError(format("statting `%1%'") % path); @@ -315,7 +315,7 @@ static void findRoots(StoreAPI & store, const Path & path, debug(format("found root `%1%' in `%2%'") % target % path); Path storePath = toStorePath(target); - if (store.isValidPath(storePath)) + if (store.isValidPath(storePath)) roots[path] = storePath; else printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") @@ -350,7 +350,7 @@ static void findRoots(StoreAPI & store, const Path & path, static Roots findRoots(StoreAPI & store, bool deleteStale) { Roots roots; - Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str()); + Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str()); findRoots(store, rootsDir, true, deleteStale, roots); return roots; } @@ -365,16 +365,16 @@ Roots LocalStore::findRoots() static void addAdditionalRoots(StoreAPI & store, PathSet & roots) { Path rootFinder = getEnv("NIX_ROOT_FINDER", - nixLibexecDir + "/nix/find-runtime-roots.pl"); + settings.nixLibexecDir + "/nix/find-runtime-roots.pl"); if (rootFinder.empty()) return; - + debug(format("executing `%1%' to find additional roots") % rootFinder); string result = runProgram(rootFinder); Strings paths = tokenizeString(result, "\n"); - + foreach (Strings::iterator, i, paths) { if (isInStore(*i)) { Path path = toStorePath(*i); @@ -557,7 +557,7 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path) } else printMsg(lvlTalkative, format("would delete `%1%'") % path); - + state.deleted.insert(path); if (state.options.action != GCOptions::gcReturnLive) state.results.paths.insert(path); @@ -605,10 +605,10 @@ void LocalStore::removeUnusedLinks() void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) { GCState state(results); - state.options = options; - - state.gcKeepOutputs = queryBoolSetting("gc-keep-outputs", false); - state.gcKeepDerivations = queryBoolSetting("gc-keep-derivations", true); + state.options = options; + + state.gcKeepOutputs = settings.gcKeepOutputs; + state.gcKeepDerivations = settings.gcKeepDerivations; /* Using `--ignore-liveness' with `--delete' can have unintended consequences if `gc-keep-outputs' or `gc-keep-derivations' are @@ -618,7 +618,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) state.gcKeepOutputs = false; state.gcKeepDerivations = false; } - + /* Acquire the global GC root. This prevents a) New roots from being added. b) Processes from creating new temporary root files. */ @@ -659,18 +659,18 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) if (!tryToDelete(state, *i)) throw Error(format("cannot delete path `%1%' since it is still alive") % *i); } - + } else { - + if (shouldDelete(state.options.action)) printMsg(lvlError, format("deleting garbage...")); else printMsg(lvlError, format("determining live/dead paths...")); - + try { - AutoCloseDir dir = opendir(nixStore.c_str()); - if (!dir) throw SysError(format("opening directory `%1%'") % nixStore); + AutoCloseDir dir = opendir(settings.nixStore.c_str()); + if (!dir) throw SysError(format("opening directory `%1%'") % settings.nixStore); /* Read the store and immediately delete all paths that aren't valid. When using --max-freed etc., deleting @@ -684,14 +684,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) checkInterrupt(); string name = dirent->d_name; if (name == "." || name == "..") continue; - Path path = nixStore + "/" + name; + Path path = settings.nixStore + "/" + name; if (isValidPath(path)) entries.push_back(path); else tryToDelete(state, path); } - dir.close(); + dir.close(); /* Now delete the unreachable valid paths. Randomise the order in which we delete entries to make the collector diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index a28e08427..7dc2e714b 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -10,38 +10,63 @@ namespace nix { -string nixStore = "/UNINIT"; -string nixDataDir = "/UNINIT"; -string nixLogDir = "/UNINIT"; -string nixStateDir = "/UNINIT"; -string nixDBPath = "/UNINIT"; -string nixConfDir = "/UNINIT"; -string nixLibexecDir = "/UNINIT"; -string nixBinDir = "/UNINIT"; - -bool keepFailed = false; -bool keepGoing = false; -bool tryFallback = false; -Verbosity buildVerbosity = lvlError; -unsigned int maxBuildJobs = 1; -unsigned int buildCores = 1; -bool readOnlyMode = false; -string thisSystem = "unset"; -time_t maxSilentTime = 0; -time_t buildTimeout = 0; -Paths substituters; -bool useBuildHook = true; -bool printBuildTrace = false; +Settings settings; -static bool settingsRead = false; +Settings::Settings() +{ + keepFailed = false; + keepGoing = false; + tryFallback = false; + buildVerbosity = lvlError; + maxBuildJobs = 1; + buildCores = 1; + readOnlyMode = false; + thisSystem = SYSTEM; + maxSilentTime = 0; + buildTimeout = 0; + useBuildHook = true; + printBuildTrace = false; + reservedSize = 1024 * 1024; + fsyncMetadata = true; + useSQLiteWAL = true; + syncBeforeRegistering = false; + useSubstitutes = true; + useChroot = false; + dirsInChroot.insert("/dev"); + dirsInChroot.insert("/dev/pts"); + impersonateLinux26 = false; + keepLog = true; + compressLog = true; + cacheFailure = false; + pollInterval = 5; + checkRootReachability = false; + gcKeepOutputs = false; + gcKeepDerivations = true; + autoOptimiseStore = true; + envKeepDerivations = false; +} -typedef std::map Settings; -static Settings settings; +void Settings::processEnvironment() +{ + nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR))); + nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR)); + nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR)); + nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR)); + nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db"); + nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR)); + nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR)); + nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR)); -/* Overriden settings. */ -Settings settingsCmdline; + string subs = getEnv("NIX_SUBSTITUTERS", "default"); + if (subs == "default") { + substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl"); + substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl"); + substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl"); + } else + substituters = tokenizeString(subs, ":"); +} string & at(Strings & ss, unsigned int n) @@ -52,7 +77,7 @@ string & at(Strings & ss, unsigned int n) } -static void readSettings() +void Settings::loadConfFile() { Path settingsFile = (format("%1%/%2%") % nixConfDir % "nix.conf").str(); if (!pathExists(settingsFile)) return; @@ -80,104 +105,88 @@ static void readSettings() Strings::iterator i = tokens.begin(); advance(i, 2); - settings[name] = Strings(i, tokens.end()); + settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow }; - - settings.insert(settingsCmdline.begin(), settingsCmdline.end()); - - settingsRead = true; } -Strings querySetting(const string & name, const Strings & def) +void Settings::set(const string & name, const string & value) { - if (!settingsRead) readSettings(); - Settings::iterator i = settings.find(name); - return i == settings.end() ? def : i->second; + settings[name] = value; } -string querySetting(const string & name, const string & def) +void Settings::update() { - Strings defs; - defs.push_back(def); - - Strings value = querySetting(name, defs); - if (value.size() != 1) - throw Error(format("configuration option `%1%' should not be a list") % name); - - return value.front(); + get(thisSystem, "system"); + get(maxBuildJobs, "build-max-jobs"); + get(buildCores, "build-cores"); + get(maxSilentTime, "build-max-silent-time"); + get(buildTimeout, "build-timeout"); + get(reservedSize, "gc-reserved-space"); + get(fsyncMetadata, "fsync-metadata"); + get(useSQLiteWAL, "use-sqlite-wal"); + get(syncBeforeRegistering, "sync-before-registering"); + get(useSubstitutes, "build-use-substitutes"); + get(buildUsersGroup, "build-users-group"); + get(useChroot, "build-use-chroot"); + get(dirsInChroot, "build-chroot-dirs"); + get(impersonateLinux26, "build-impersonate-linux-26"); + get(keepLog, "build-keep-log"); + get(compressLog, "build-compress-log"); + get(cacheFailure, "build-cache-failure"); + get(pollInterval, "build-poll-interval"); + get(checkRootReachability, "gc-check-reachability"); + get(gcKeepOutputs, "gc-keep-outputs"); + get(gcKeepDerivations, "gc-keep-derivations"); + get(autoOptimiseStore, "auto-optimise-store"); + get(envKeepDerivations, "env-keep-derivations"); } -bool queryBoolSetting(const string & name, bool def) +void Settings::get(string & res, const string & name) { - string v = querySetting(name, def ? "true" : "false"); - if (v == "true") return true; - else if (v == "false") return false; + SettingsMap::iterator i = settings.find(name); + if (i == settings.end()) return; + res = i->second; +} + + +void Settings::get(bool & res, const string & name) +{ + SettingsMap::iterator i = settings.find(name); + if (i == settings.end()) return; + if (i->second == "true") res = true; + else if (i->second == "false") res = false; else throw Error(format("configuration option `%1%' should be either `true' or `false', not `%2%'") - % name % v); + % name % i->second); } -unsigned int queryIntSetting(const string & name, unsigned int def) +void Settings::get(PathSet & res, const string & name) { - int n; - if (!string2Int(querySetting(name, int2String(def)), n) || n < 0) + SettingsMap::iterator i = settings.find(name); + if (i == settings.end()) return; + res.clear(); + Strings ss = tokenizeString(i->second); + res.insert(ss.begin(), ss.end()); +} + + +template void Settings::get(N & res, const string & name) +{ + SettingsMap::iterator i = settings.find(name); + if (i == settings.end()) return; + if (!string2Int(i->second, res)) throw Error(format("configuration setting `%1%' should have an integer value") % name); - return n; } -void overrideSetting(const string & name, const Strings & value) -{ - if (settingsRead) settings[name] = value; - settingsCmdline[name] = value; -} - - -void reloadSettings() -{ - settingsRead = false; - settings.clear(); -} - - -void setDefaultsFromEnvironment() -{ - /* Setup Nix paths. */ - nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR))); - nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR)); - nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR)); - nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR)); - nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db"); - nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR)); - nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR)); - nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR)); - - string subs = getEnv("NIX_SUBSTITUTERS", "default"); - if (subs == "default") { - substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl"); - substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl"); - substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl"); - } else - substituters = tokenizeString(subs, ":"); - - /* Get some settings from the configuration file. */ - thisSystem = querySetting("system", SYSTEM); - maxBuildJobs = queryIntSetting("build-max-jobs", 1); - buildCores = queryIntSetting("build-cores", 1); - maxSilentTime = queryIntSetting("build-max-silent-time", 0); - buildTimeout = queryIntSetting("build-timeout", 0); -} - - -string packSettings() +string Settings::pack() { string s; - if (!settingsRead) readSettings(); - foreach (Settings::iterator, i, settings) { - s += i->first; s += '='; s += concatStringsSep(" ", i->second); s += '\n'; + foreach (SettingsMap::iterator, i, settings) { + s += i->first; s += '='; s += i->second; s += '\n'; } return s; } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 30acf59ef..5783d9bf3 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -2,120 +2,189 @@ #include "types.hh" +#include + namespace nix { -/* Path names. */ +struct Settings { -/* nixStore is the directory where we generally store atomic and - derived files. */ -extern string nixStore; + Settings(); -extern string nixDataDir; /* !!! fix */ + void processEnvironment(); -/* nixLogDir is the directory where we log various operations. */ -extern string nixLogDir; + void loadConfFile(); -/* nixStateDir is the directory where state is stored. */ -extern string nixStateDir; + void set(const string & name, const string & value); -/* nixDBPath is the path name of our Berkeley DB environment. */ -extern string nixDBPath; + void update(); -/* nixConfDir is the directory where configuration files are - stored. */ -extern string nixConfDir; + string pack(); -/* nixLibexecDir is the directory where internal helper programs are - stored. */ -extern string nixLibexecDir; + /* The directory where we store sources and derived files. */ + Path nixStore; -/* nixBinDir is the directory where the main programs are stored. */ -extern string nixBinDir; + Path nixDataDir; /* !!! fix */ + + /* The directory where we log various operations. */ + Path nixLogDir; + + /* The directory where state is stored. */ + Path nixStateDir; + + /* The directory where we keep the SQLite database. */ + Path nixDBPath; + + /* The directory where configuration files are stored. */ + Path nixConfDir; + + /* The directory where internal helper programs are stored. */ + Path nixLibexecDir; + + /* The directory where the main programs are stored. */ + Path nixBinDir; + + /* Whether to keep temporary directories of failed builds. */ + bool keepFailed; + + /* Whether to keep building subgoals when a sibling (another + subgoal of the same goal) fails. */ + bool keepGoing; + + /* Whether, if we cannot realise the known closure corresponding + to a derivation, we should try to normalise the derivation + instead. */ + bool tryFallback; + + /* Verbosity level for build output. */ + Verbosity buildVerbosity; + + /* Maximum number of parallel build jobs. 0 means unlimited. */ + unsigned int maxBuildJobs; + + /* Number of CPU cores to utilize in parallel within a build, + i.e. by passing this number to Make via '-j'. 0 means that the + number of actual CPU cores on the local host ought to be + auto-detected. */ + unsigned int buildCores; + + /* Read-only mode. Don't copy stuff to the store, don't change + the database. */ + bool readOnlyMode; + + /* The canonical system name, as returned by config.guess. */ + string thisSystem; + + /* The maximum time in seconds that a builer can go without + producing any output on stdout/stderr before it is killed. 0 + means infinity. */ + time_t maxSilentTime; + + /* The maximum duration in seconds that a builder can run. 0 + means infinity. */ + time_t buildTimeout; + + /* The substituters. There are programs that can somehow realise + a store path without building, e.g., by downloading it or + copying it from a CD. */ + Paths substituters; + + /* Whether to use build hooks (for distributed builds). Sometimes + users want to disable this from the command-line. */ + bool useBuildHook; + + /* Whether buildDerivations() should print out lines on stderr in + a fixed format to allow its progress to be monitored. Each + line starts with a "@". The following are defined: + + @ build-started + @ build-failed + @ build-succeeded + @ substituter-started + @ substituter-failed + @ substituter-succeeded + + Best combined with --no-build-output, otherwise stderr might + conceivably contain lines in this format printed by the + builders. */ + bool printBuildTrace; + + /* Amount of reserved space for the garbage collector + (/nix/var/nix/db/reserved). */ + off_t reservedSize; + + /* Whether SQLite should use fsync. */ + bool fsyncMetadata; + + /* Whether SQLite should use WAL mode. */ + bool useSQLiteWAL; + + /* Whether to call sync() before registering a path as valid. */ + bool syncBeforeRegistering; + + /* Whether to use substitutes. */ + bool useSubstitutes; + + /* The Unix group that contains the build users. */ + string buildUsersGroup; + + /* Whether to build in chroot. */ + bool useChroot; + + /* The directories from the host filesystem to be included in the + chroot. */ + PathSet dirsInChroot; + + /* Whether to impersonate a Linux 2.6 machine on newer kernels. */ + bool impersonateLinux26; + + /* Whether to store build logs. */ + bool keepLog; + + /* Whether to compress logs. */ + bool compressLog; + + /* Whether to cache build failures. */ + bool cacheFailure; + + /* How often (in seconds) to poll for locks. */ + unsigned int pollInterval; + + /* Whether to check if new GC roots can in fact be found by the + garbage collector. */ + bool checkRootReachability; + + /* Whether the garbage collector should keep outputs of live + derivations. */ + bool gcKeepOutputs; + + /* Whether the garbage collector should keep derivers of live + paths. */ + bool gcKeepDerivations; + + /* Whether to automatically replace files with identical contents + with hard links. */ + bool autoOptimiseStore; + + /* Whether to add derivations as a dependency of user environments + (to prevent them from being GCed). */ + bool envKeepDerivations; + +private: + typedef std::map SettingsMap; + + SettingsMap settings; + + void get(string & res, const string & name); + void get(bool & res, const string & name); + void get(PathSet & res, const string & name); + template void get(N & res, const string & name); +}; -/* Misc. global flags. */ - -/* Whether to keep temporary directories of failed builds. */ -extern bool keepFailed; - -/* Whether to keep building subgoals when a sibling (another subgoal - of the same goal) fails. */ -extern bool keepGoing; - -/* Whether, if we cannot realise the known closure corresponding to a - derivation, we should try to normalise the derivation instead. */ -extern bool tryFallback; - -/* Verbosity level for build output. */ -extern Verbosity buildVerbosity; - -/* Maximum number of parallel build jobs. 0 means unlimited. */ -extern unsigned int maxBuildJobs; - -/* Number of CPU cores to utilize in parallel within a build, i.e. by passing - this number to Make via '-j'. 0 means that the number of actual CPU cores on - the local host ought to be auto-detected. */ -extern unsigned int buildCores; - -/* Read-only mode. Don't copy stuff to the store, don't change the - database. */ -extern bool readOnlyMode; - -/* The canonical system name, as returned by config.guess. */ -extern string thisSystem; - -/* The maximum time in seconds that a builer can go without producing - any output on stdout/stderr before it is killed. 0 means - infinity. */ -extern time_t maxSilentTime; - -/* The maximum duration in seconds that a builder can run. 0 means - infinity. */ -extern time_t buildTimeout; - -/* The substituters. There are programs that can somehow realise a - store path without building, e.g., by downloading it or copying it - from a CD. */ -extern Paths substituters; - -/* Whether to use build hooks (for distributed builds). Sometimes - users want to disable this from the command-line. */ -extern bool useBuildHook; - -/* Whether buildDerivations() should print out lines on stderr in a - fixed format to allow its progress to be monitored. Each line - starts with a "@". The following are defined: - - @ build-started - @ build-failed - @ build-succeeded - @ substituter-started - @ substituter-failed - @ substituter-succeeded - - Best combined with --no-build-output, otherwise stderr might - conceivably contain lines in this format printed by the builders. -*/ -extern bool printBuildTrace; - - -Strings querySetting(const string & name, const Strings & def); - -string querySetting(const string & name, const string & def); - -bool queryBoolSetting(const string & name, bool def); - -unsigned int queryIntSetting(const string & name, unsigned int def); - -void overrideSetting(const string & name, const Strings & value); - -void reloadSettings(); - -void setDefaultsFromEnvironment(); - -string packSettings(); +// FIXME: don't use a global variable. +extern Settings settings; } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index aaa1abb56..f20324d4e 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -181,7 +181,7 @@ struct SQLiteTxn void checkStoreNotSymlink() { if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return; - Path path = nixStore; + Path path = settings.nixStore; struct stat st; while (path != "/") { if (lstat(path.c_str(), &st)) @@ -198,21 +198,21 @@ void checkStoreNotSymlink() LocalStore::LocalStore(bool reserveSpace) { - schemaPath = nixDBPath + "/schema"; + schemaPath = settings.nixDBPath + "/schema"; - if (readOnlyMode) { + if (settings.readOnlyMode) { openDB(false); return; } /* Create missing state directories if they don't already exist. */ - createDirs(nixStore); - createDirs(linksDir = nixStore + "/.links"); - Path profilesDir = nixStateDir + "/profiles"; - createDirs(nixStateDir + "/profiles"); - createDirs(nixStateDir + "/temproots"); - createDirs(nixDBPath); - Path gcRootsDir = nixStateDir + "/gcroots"; + createDirs(settings.nixStore); + createDirs(linksDir = settings.nixStore + "/.links"); + Path profilesDir = settings.nixStateDir + "/profiles"; + createDirs(settings.nixStateDir + "/profiles"); + createDirs(settings.nixStateDir + "/temproots"); + createDirs(settings.nixDBPath); + Path gcRootsDir = settings.nixStateDir + "/gcroots"; if (!pathExists(gcRootsDir)) { createDirs(gcRootsDir); if (symlink(profilesDir.c_str(), (gcRootsDir + "/profiles").c_str()) == -1) @@ -226,13 +226,12 @@ LocalStore::LocalStore(bool reserveSpace) needed, we reserve some dummy space that we can free just before doing a garbage collection. */ try { - Path reservedPath = nixDBPath + "/reserved"; + Path reservedPath = settings.nixDBPath + "/reserved"; if (reserveSpace) { - int reservedSize = queryIntSetting("gc-reserved-space", 1024 * 1024); struct stat st; if (stat(reservedPath.c_str(), &st) == -1 || - st.st_size != reservedSize) - writeFile(reservedPath, string(reservedSize, 'X')); + st.st_size != settings.reservedSize) + writeFile(reservedPath, string(settings.reservedSize, 'X')); } else deletePath(reservedPath); @@ -242,11 +241,11 @@ LocalStore::LocalStore(bool reserveSpace) /* Acquire the big fat lock in shared mode to make sure that no schema upgrade is in progress. */ try { - Path globalLockPath = nixDBPath + "/big-lock"; + Path globalLockPath = settings.nixDBPath + "/big-lock"; globalLock = openLockFile(globalLockPath.c_str(), true); } catch (SysError & e) { if (e.errNo != EACCES) throw; - readOnlyMode = true; + settings.readOnlyMode = true; openDB(false); return; } @@ -325,7 +324,7 @@ int LocalStore::getSchema() void LocalStore::openDB(bool create) { /* Open the Nix database. */ - if (sqlite3_open_v2((nixDBPath + "/db.sqlite").c_str(), &db.db, + if (sqlite3_open_v2((settings.nixDBPath + "/db.sqlite").c_str(), &db.db, SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK) throw Error("cannot open SQLite database"); @@ -342,13 +341,13 @@ void LocalStore::openDB(bool create) should be safe enough. If the user asks for it, don't sync at all. This can cause database corruption if the system crashes. */ - string syncMode = queryBoolSetting("fsync-metadata", true) ? "normal" : "off"; + string syncMode = settings.fsyncMetadata ? "normal" : "off"; if (sqlite3_exec(db, ("pragma synchronous = " + syncMode + ";").c_str(), 0, 0, 0) != SQLITE_OK) throwSQLiteError(db, "setting synchronous mode"); /* Set the SQLite journal mode. WAL mode is fastest, so it's the default. */ - string mode = queryBoolSetting("use-sqlite-wal", true) ? "wal" : "truncate"; + string mode = settings.useSQLiteWAL ? "wal" : "truncate"; string prevMode; { SQLiteStmt stmt; @@ -890,7 +889,7 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart) SQLiteTxn txn(db); - Path prefix = nixStore + "/" + hashPart; + Path prefix = settings.nixStore + "/" + hashPart; SQLiteStmtUse use(stmtQueryPathFromHashPart); stmtQueryPathFromHashPart.bind(prefix); @@ -933,7 +932,7 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter & /* 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); fromPipe.readSide.close(); toPipe.writeSide.close(); @@ -969,7 +968,7 @@ template T getIntLine(int fd) PathSet LocalStore::querySubstitutablePaths(const PathSet & paths) { PathSet res; - foreach (Paths::iterator, i, substituters) { + foreach (Paths::iterator, i, settings.substituters) { if (res.size() == paths.size()) break; RunningSubstituter & run(runningSubstituters[*i]); startSubstituter(*i, run); @@ -1023,7 +1022,7 @@ void LocalStore::querySubstitutablePathInfos(const PathSet & paths, SubstitutablePathInfos & infos) { PathSet todo = paths; - foreach (Paths::iterator, i, substituters) { + foreach (Paths::iterator, i, settings.substituters) { if (todo.empty()) break; querySubstitutablePathInfos(*i, todo, infos); } @@ -1046,11 +1045,10 @@ void LocalStore::registerValidPath(const ValidPathInfo & info) void LocalStore::registerValidPaths(const ValidPathInfos & infos) { - /* sqlite will fsync by default, but the new valid paths may not be fsync-ed. + /* SQLite will fsync by default, but the new valid paths may not be fsync-ed. * So some may want to fsync them before registering the validity, at the * expense of some speed of the path registering operation. */ - if (queryBoolSetting("sync-before-registering", false)) - sync(); + if (settings.syncBeforeRegistering) sync(); while (1) { try { @@ -1294,7 +1292,7 @@ void LocalStore::exportPath(const Path & path, bool sign, Path hashFile = tmpDir + "/hash"; writeFile(hashFile, printHash(hash)); - Path secretKey = nixConfDir + "/signing-key.sec"; + Path secretKey = settings.nixConfDir + "/signing-key.sec"; checkSecrecy(secretKey); Strings args; @@ -1340,7 +1338,7 @@ Path LocalStore::createTempDirInStore() /* There is a slight possibility that `tmpDir' gets deleted by the GC between createTempDir() and addTempRoot(), so repeat until `tmpDir' exists. */ - tmpDir = createTempDir(nixStore); + tmpDir = createTempDir(settings.nixStore); addTempRoot(tmpDir); } while (!pathExists(tmpDir)); return tmpDir; @@ -1392,7 +1390,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source) args.push_back("rsautl"); args.push_back("-verify"); args.push_back("-inkey"); - args.push_back(nixConfDir + "/signing-key.pub"); + args.push_back(settings.nixConfDir + "/signing-key.pub"); args.push_back("-pubin"); args.push_back("-in"); args.push_back(sigFile); @@ -1501,7 +1499,7 @@ void LocalStore::verifyStore(bool checkContents) /* Acquire the global GC lock to prevent a garbage collection. */ AutoCloseFD fdGCLock = openGCLock(ltWrite); - Paths entries = readDirectory(nixStore); + Paths entries = readDirectory(settings.nixStore); PathSet store(entries.begin(), entries.end()); /* Check whether all valid paths actually exist. */ @@ -1611,9 +1609,9 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store, PathSet LocalStore::queryValidPathsOld() { PathSet paths; - Strings entries = readDirectory(nixDBPath + "/info"); + Strings entries = readDirectory(settings.nixDBPath + "/info"); foreach (Strings::iterator, i, entries) - if (i->at(0) != '.') paths.insert(nixStore + "/" + *i); + if (i->at(0) != '.') paths.insert(settings.nixStore + "/" + *i); return paths; } @@ -1625,7 +1623,7 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path) /* Read the info file. */ string baseName = baseNameOf(path); - Path infoFile = (format("%1%/info/%2%") % nixDBPath % baseName).str(); + Path infoFile = (format("%1%/info/%2%") % settings.nixDBPath % baseName).str(); if (!pathExists(infoFile)) throw Error(format("path `%1%' is not valid") % path); string info = readFile(infoFile); diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 4761658ed..7cf9fc18d 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -75,7 +75,7 @@ struct SQLiteStmt void bind64(long long value); void bind(); }; - + class LocalStore : public StoreAPI { @@ -84,7 +84,7 @@ private: RunningSubstituters runningSubstituters; Path linksDir; - + public: /* Initialise the local store, upgrading the schema if @@ -92,15 +92,15 @@ public: LocalStore(bool reserveSpace = true); ~LocalStore(); - + /* Implementations of abstract store API methods. */ - + bool isValidPath(const Path & path); PathSet queryValidPaths(const PathSet & paths); - + PathSet queryAllValidPaths(); - + ValidPathInfo queryPathInfo(const Path & path); Hash queryPathHash(const Path & path); @@ -120,17 +120,17 @@ public: PathSet queryDerivationOutputs(const Path & path); StringSet queryDerivationOutputNames(const Path & path); - + Path queryPathFromHashPart(const string & hashPart); - + PathSet querySubstitutablePaths(const PathSet & paths); void querySubstitutablePathInfos(const Path & substituter, PathSet & paths, SubstitutablePathInfos & infos); - + void querySubstitutablePathInfos(const PathSet & paths, SubstitutablePathInfos & infos); - + Path addToStore(const Path & srcPath, bool recursive = true, HashType hashAlgo = htSHA256, PathFilter & filter = defaultPathFilter); @@ -149,7 +149,7 @@ public: Sink & sink); Paths importPaths(bool requireSignature, Source & source); - + void buildPaths(const PathSet & paths); void ensurePath(const Path & path); @@ -157,7 +157,7 @@ public: void addTempRoot(const Path & path); void addIndirectRoot(const Path & path); - + void syncWithGC(); Roots findRoots(); @@ -170,7 +170,7 @@ public: /* Optimise a single store path. */ void optimisePath(const Path & path); - + /* Check the integrity of the Nix store. */ void verifyStore(bool checkContents); @@ -229,18 +229,18 @@ private: unsigned long long queryValidPathId(const Path & path); unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true); - + void addReference(unsigned long long referrer, unsigned long long reference); - + void appendReferrer(const Path & from, const Path & to, bool lock); - + void rewriteReferrers(const Path & path, bool purge, PathSet referrers); void invalidatePath(const Path & path); /* Delete a path from the Nix store. */ void invalidatePathChecked(const Path & path); - + void verifyPath(const Path & path, const PathSet & store, PathSet & done, PathSet & validPaths); @@ -253,14 +253,14 @@ private: struct GCState; void deleteGarbage(GCState & state, const Path & path); - + bool tryToDelete(GCState & state, const Path & path); - + bool isActiveTempFile(const GCState & state, const Path & path, const string & suffix); - + int openGCLock(LockType lockType); - + void removeUnusedLinks(); void startSubstituter(const Path & substituter, @@ -269,7 +269,7 @@ private: Path createTempDirInStore(); Path importPath(bool requireSignature, Source & source); - + void checkDerivationOutputs(const Path & drvPath, const Derivation & drv); void optimisePath_(OptimiseStats & stats, const Path & path); @@ -290,9 +290,6 @@ void canonicalisePathMetaData(const Path & path, bool recurse); MakeError(PathInUse, Error); -/* Whether we are in build users mode. */ -bool haveBuildUsers(); - /* Whether we are root. */ bool amPrivileged(); @@ -305,5 +302,5 @@ void deletePathWrapped(const Path & path, unsigned long long & bytesFreed, unsigned long long & blocksFreed); void deletePathWrapped(const Path & path); - + } diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index aa5f6ff72..3ce300e30 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -52,10 +52,8 @@ void queryMissing(StoreAPI & store, const PathSet & targets, unsigned long long & downloadSize, unsigned long long & narSize) { downloadSize = narSize = 0; - - PathSet todo(targets.begin(), targets.end()), done; - bool useSubstitutes = queryBoolSetting("build-use-substitutes", true); + PathSet todo(targets.begin(), targets.end()), done; /* Getting substitute info has high latency when using the binary cache substituter. Thus it's essential to do substitute @@ -77,7 +75,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets, */ while (!todo.empty()) { - + PathSet query, todoDrv, todoNonDrv; foreach (PathSet::iterator, i, todo) { @@ -96,9 +94,9 @@ void queryMissing(StoreAPI & store, const PathSet & targets, foreach (DerivationOutputs::iterator, j, drv.outputs) if (!store.isValidPath(j->second.path)) invalid.insert(j->second.path); if (invalid.empty()) continue; - + todoDrv.insert(*i); - if (useSubstitutes) query.insert(invalid.begin(), invalid.end()); + if (settings.useSubstitutes) query.insert(invalid.begin(), invalid.end()); } else { @@ -109,7 +107,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets, } todo.clear(); - + SubstitutablePathInfos infos; store.querySubstitutablePathInfos(query, infos); @@ -118,7 +116,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets, Derivation drv = derivationFromPath(store, *i); bool mustBuild = false; - if (useSubstitutes) { + if (settings.useSubstitutes) { foreach (DerivationOutputs::iterator, j, drv.outputs) if (!store.isValidPath(j->second.path) && infos.find(j->second.path) == infos.end()) @@ -135,7 +133,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets, foreach (DerivationOutputs::iterator, i, drv.outputs) todoNonDrv.insert(i->second.path); } - + foreach (PathSet::iterator, i, todoNonDrv) { done.insert(*i); SubstitutablePathInfos::iterator info = infos.find(*i); @@ -150,22 +148,22 @@ void queryMissing(StoreAPI & store, const PathSet & targets, } } - + static void dfsVisit(StoreAPI & store, const PathSet & paths, const Path & path, PathSet & visited, Paths & sorted, PathSet & parents) { if (parents.find(path) != parents.end()) throw BuildError(format("cycle detected in the references of `%1%'") % path); - + if (visited.find(path) != visited.end()) return; visited.insert(path); parents.insert(path); - + PathSet references; if (store.isValidPath(path)) store.queryReferences(path, references); - + foreach (PathSet::iterator, i, references) /* Don't traverse into paths that don't exist. That can happen due to substitutes for non-existent paths. */ diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 84a72604b..e08ee1784 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -17,7 +17,7 @@ static void makeWritable(const Path & path) { struct stat st; if (lstat(path.c_str(), &st)) - throw SysError(format("getting attributes of path `%1%'") % path); + throw SysError(format("getting attributes of path `%1%'") % path); if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path); if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1) throw SysError(format("changing writability of `%1%'") % path); @@ -53,15 +53,15 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) { struct stat st; if (lstat(path.c_str(), &st)) - throw SysError(format("getting attributes of path `%1%'") % path); + throw SysError(format("getting attributes of path `%1%'") % path); if (S_ISDIR(st.st_mode)) { Strings names = readDirectory(path); - foreach (Strings::iterator, i, names) - optimisePath_(stats, path + "/" + *i); + foreach (Strings::iterator, i, names) + optimisePath_(stats, path + "/" + *i); return; } - + /* We can hard link regular files and maybe symlinks. */ if (!S_ISREG(st.st_mode) #if CAN_LINK_SYMLINK @@ -69,7 +69,7 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) && !S_ISLNK(st.st_mode) #endif ) return; - + /* Sometimes SNAFUs can cause files in the Nix store to be modified, in particular when running programs as root under NixOS (example: $fontconfig/var/cache being modified). Skip @@ -110,25 +110,25 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) current file with a hard link to that file. */ struct stat stLink; if (lstat(linkPath.c_str(), &stLink)) - throw SysError(format("getting attributes of path `%1%'") % linkPath); - + throw SysError(format("getting attributes of path `%1%'") % linkPath); + stats.sameContents++; if (st.st_ino == stLink.st_ino) { printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % linkPath); return; } - + printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % linkPath); Path tempLink = (format("%1%/.tmp-link-%2%-%3%") - % nixStore % getpid() % rand()).str(); + % settings.nixStore % getpid() % rand()).str(); /* Make the containing directory writable, but only if it's not the store itself (we don't want or need to mess with its permissions). */ bool mustToggle = !isStorePath(path); if (mustToggle) makeWritable(dirOf(path)); - + /* When we're done, make the directory read-only again and reset its timestamp back to 0. */ MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : ""); @@ -192,10 +192,8 @@ void LocalStore::optimiseStore(OptimiseStats & stats) void LocalStore::optimisePath(const Path & path) { - if (queryBoolSetting("auto-optimise-store", true)) { - OptimiseStats stats; - optimisePath_(stats, path); - } + OptimiseStats stats; + if (settings.autoOptimiseStore) optimisePath_(stats, path); } diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 5910ffd53..c67e53bfb 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -100,7 +100,7 @@ void RemoteStore::forkSlave() /* Start the worker. */ Path worker = getEnv("NIX_WORKER"); if (worker == "") - worker = nixBinDir + "/nix-worker"; + worker = settings.nixBinDir + "/nix-worker"; child = fork(); @@ -142,7 +142,7 @@ void RemoteStore::connectToDaemon() if (fdSocket == -1) throw SysError("cannot create Unix domain socket"); - string socketPath = nixStateDir + DEFAULT_SOCKET_PATH; + string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH; /* Urgh, sockaddr_un allows path names of only 108 characters. So chdir to the socket directory so that we can pass a relative @@ -184,23 +184,23 @@ RemoteStore::~RemoteStore() void RemoteStore::setOptions() { writeInt(wopSetOptions, to); - writeInt(keepFailed, to); - writeInt(keepGoing, to); - writeInt(tryFallback, to); + writeInt(settings.keepFailed, to); + writeInt(settings.keepGoing, to); + writeInt(settings.tryFallback, to); writeInt(verbosity, to); - writeInt(maxBuildJobs, to); - writeInt(maxSilentTime, to); + writeInt(settings.maxBuildJobs, to); + writeInt(settings.maxSilentTime, to); if (GET_PROTOCOL_MINOR(daemonVersion) >= 2) - writeInt(useBuildHook, to); + writeInt(settings.useBuildHook, to); if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) { - writeInt(buildVerbosity, to); + writeInt(settings.buildVerbosity, to); writeInt(logType, to); - writeInt(printBuildTrace, to); + writeInt(settings.printBuildTrace, to); } if (GET_PROTOCOL_MINOR(daemonVersion) >= 6) - writeInt(buildCores, to); + writeInt(settings.buildCores, to); if (GET_PROTOCOL_MINOR(daemonVersion) >= 10) - writeInt(queryBoolSetting("build-use-substitutes", true), to); + writeInt(settings.useSubstitutes, to); processStderr(); } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index b64988268..6f81a9aab 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -19,16 +19,16 @@ GCOptions::GCOptions() bool isInStore(const Path & path) { return path[0] == '/' - && string(path, 0, nixStore.size()) == nixStore - && path.size() >= nixStore.size() + 2 - && path[nixStore.size()] == '/'; + && string(path, 0, settings.nixStore.size()) == settings.nixStore + && path.size() >= settings.nixStore.size() + 2 + && path[settings.nixStore.size()] == '/'; } bool isStorePath(const Path & path) { return isInStore(path) - && path.find('/', nixStore.size() + 1) == Path::npos; + && path.find('/', settings.nixStore.size() + 1) == Path::npos; } @@ -43,7 +43,7 @@ Path toStorePath(const Path & path) { if (!isInStore(path)) throw Error(format("path `%1%' is not in the Nix store") % path); - Path::size_type slash = path.find('/', nixStore.size() + 1); + Path::size_type slash = path.find('/', settings.nixStore.size() + 1); if (slash == Path::npos) return path; else @@ -74,7 +74,7 @@ Path followLinksToStorePath(const Path & path) string storePathToName(const Path & path) { assertStorePath(path); - return string(path, nixStore.size() + 34); + return string(path, settings.nixStore.size() + 34); } @@ -173,11 +173,11 @@ Path makeStorePath(const string & type, { /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */ string s = type + ":sha256:" + printHash(hash) + ":" - + nixStore + ":" + name; + + settings.nixStore + ":" + name; checkStoreName(name); - return nixStore + "/" + return settings.nixStore + "/" + printHash32(compressHash(hashString(htSHA256, s), 20)) + "-" + name; } diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index f06f23dad..475bdd366 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -55,7 +55,6 @@ struct Globals EvalState state; bool dryRun; bool preserveInstalled; - bool keepDerivations; string forceName; bool prebuiltOnly; }; @@ -266,8 +265,8 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, if (k != newest.end()) { d = j->first.system == k->second.first.system ? 0 : - j->first.system == thisSystem ? 1 : - k->second.first.system == thisSystem ? -1 : 0; + j->first.system == settings.thisSystem ? 1 : + k->second.first.system == settings.thisSystem ? -1 : 0; if (d == 0) d = comparePriorities(state, j->first, k->second.first); if (d == 0) @@ -498,7 +497,7 @@ static void installDerivations(Globals & globals, if (globals.dryRun) return; if (createUserEnv(globals.state, allElems, - profile, globals.keepDerivations, lockToken)) break; + profile, settings.envKeepDerivations, lockToken)) break; } } @@ -605,7 +604,7 @@ static void upgradeDerivations(Globals & globals, if (globals.dryRun) return; if (createUserEnv(globals.state, newElems, - globals.profile, globals.keepDerivations, lockToken)) break; + globals.profile, settings.envKeepDerivations, lockToken)) break; } } @@ -672,7 +671,7 @@ static void opSetFlag(Globals & globals, /* Write the new user environment. */ if (createUserEnv(globals.state, installedElems, - globals.profile, globals.keepDerivations, lockToken)) break; + globals.profile, settings.envKeepDerivations, lockToken)) break; } } @@ -740,7 +739,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors, if (globals.dryRun) return; if (createUserEnv(globals.state, newElems, - profile, globals.keepDerivations, lockToken)) break; + profile, settings.envKeepDerivations, lockToken)) break; } } @@ -869,7 +868,7 @@ static void opQuery(Globals & globals, enum { sInstalled, sAvailable } source = sInstalled; - readOnlyMode = true; /* makes evaluation a bit faster */ + settings.readOnlyMode = true; /* makes evaluation a bit faster */ for (Strings::iterator i = args.begin(); i != args.end(); ) { string arg = *i++; @@ -1262,9 +1261,6 @@ void run(Strings args) globals.preserveInstalled = false; globals.prebuiltOnly = false; - globals.keepDerivations = - queryBoolSetting("env-keep-derivations", false); - for (Strings::iterator i = args.begin(); i != args.end(); ) { string arg = *i++; @@ -1331,7 +1327,7 @@ void run(Strings args) Path profileLink = getHomeDir() + "/.nix-profile"; globals.profile = pathExists(profileLink) ? absPath(readLink(profileLink), dirOf(profileLink)) - : canonPath(nixStateDir + "/profiles/default"); + : canonPath(settings.nixStateDir + "/profiles/default"); } store = openStore(); diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index d86c9fc84..270b4ddc2 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -94,11 +94,11 @@ void run(Strings args) if (arg == "-") readStdin = true; else if (arg == "--eval-only") { - readOnlyMode = true; + settings.readOnlyMode = true; evalOnly = true; } else if (arg == "--parse-only") { - readOnlyMode = true; + settings.readOnlyMode = true; parseOnly = evalOnly = true; } else if (arg == "--find-file") diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 941301d2e..5ada79713 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -47,7 +47,7 @@ LocalStore & ensureLocalStore() static Path useDeriver(Path path) -{ +{ if (!isDerivation(path)) { path = store->queryDeriver(path); if (path == "") @@ -89,18 +89,18 @@ static PathSet realisePath(const Path & path) static void opRealise(Strings opFlags, Strings opArgs) { bool dryRun = false; - + foreach (Strings::iterator, i, opFlags) if (*i == "--dry-run") dryRun = true; else throw UsageError(format("unknown flag `%1%'") % *i); foreach (Strings::iterator, i, opArgs) *i = followLinksToStorePath(*i); - + printMissing(*store, PathSet(opArgs.begin(), opArgs.end())); - + if (dryRun) return; - + /* Build all paths at the same time to exploit parallelism. */ PathSet paths(opArgs.begin(), opArgs.end()); store->buildPaths(paths); @@ -128,7 +128,7 @@ static void opAdd(Strings opFlags, Strings opArgs) static void opAddFixed(Strings opFlags, Strings opArgs) { bool recursive = false; - + for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ++i) if (*i == "--recursive") recursive = true; @@ -136,7 +136,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs) if (opArgs.empty()) throw UsageError("first argument must be hash algorithm"); - + HashType hashAlgo = parseHashType(opArgs.front()); opArgs.pop_front(); @@ -149,7 +149,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs) static void opPrintFixedPath(Strings opFlags, Strings opArgs) { bool recursive = false; - + for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ++i) if (*i == "--recursive") recursive = true; @@ -157,7 +157,7 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs) if (opArgs.size() != 3) throw UsageError(format("`--print-fixed-path' requires three arguments")); - + Strings::iterator i = opArgs.begin(); HashType hashAlgo = parseHashType(*i++); string hash = *i++; @@ -205,12 +205,12 @@ static void printTree(const Path & path, PathSet references; store->queryReferences(path, references); - -#if 0 + +#if 0 for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i) cout << format("%1%%2%\n") % (tailPad + treeConn) % *i; -#endif +#endif /* Topologically sort under the relation A < B iff A \in closure(B). That is, if derivation A is an (possibly indirect) @@ -266,7 +266,7 @@ static void opQuery(Strings opFlags, Strings opArgs) else throw UsageError(format("unknown flag `%1%'") % *i); switch (query) { - + case qOutputs: { foreach (Strings::iterator, i, opArgs) { *i = followLinksToStorePath(*i); @@ -293,7 +293,7 @@ static void opQuery(Strings opFlags, Strings opArgs) } } Paths sorted = topoSortPaths(*store, paths); - for (Paths::reverse_iterator i = sorted.rbegin(); + for (Paths::reverse_iterator i = sorted.rbegin(); i != sorted.rend(); ++i) cout << format("%s\n") % *i; break; @@ -328,7 +328,7 @@ static void opQuery(Strings opFlags, Strings opArgs) if (query == qHash) { assert(info.hash.type == htSHA256); cout << format("sha256:%1%\n") % printHash32(info.hash); - } else if (query == qSize) + } else if (query == qSize) cout << format("%1%\n") % info.narSize; } } @@ -340,7 +340,7 @@ static void opQuery(Strings opFlags, Strings opArgs) printTree(followLinksToStorePath(*i), "", "", done); break; } - + case qGraph: { PathSet roots; foreach (Strings::iterator, i, opArgs) { @@ -366,7 +366,7 @@ static void opQuery(Strings opFlags, Strings opArgs) cout << format("%1%\n") % followLinksToStorePath(*i); break; } - + case qRoots: { PathSet referrers; foreach (Strings::iterator, i, opArgs) { @@ -380,7 +380,7 @@ static void opQuery(Strings opFlags, Strings opArgs) cout << format("%1%\n") % i->first; break; } - + default: abort(); } @@ -426,9 +426,9 @@ static void opReadLog(Strings opFlags, Strings opArgs) foreach (Strings::iterator, i, opArgs) { Path path = useDeriver(followLinksToStorePath(*i)); - + Path logPath = (format("%1%/%2%/%3%") % - nixLogDir % drvsLogDir % baseNameOf(path)).str(); + settings.nixLogDir % drvsLogDir % baseNameOf(path)).str(); Path logBz2Path = logPath + ".bz2"; if (pathExists(logPath)) { @@ -454,7 +454,7 @@ static void opReadLog(Strings opFlags, Strings opArgs) } while (err != BZ_STREAM_END); BZ2_bzReadClose(&err, bz); } - + else throw Error(format("build log of derivation `%1%' is not available") % path); } } @@ -474,7 +474,7 @@ static void opDumpDB(Strings opFlags, Strings opArgs) static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) { ValidPathInfos infos; - + while (1) { ValidPathInfo info = decodeValidPathInfo(cin, hashGiven); if (info.path == "") break; @@ -508,7 +508,7 @@ static void opRegisterValidity(Strings opFlags, Strings opArgs) { bool reregister = false; // !!! maybe this should be the default bool hashGiven = false; - + for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ++i) if (*i == "--reregister") reregister = true; @@ -524,7 +524,7 @@ static void opRegisterValidity(Strings opFlags, Strings opArgs) static void opCheckValidity(Strings opFlags, Strings opArgs) { bool printInvalid = false; - + for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ++i) if (*i == "--print-invalid") printInvalid = true; @@ -551,13 +551,13 @@ static string showBytes(unsigned long long bytes, unsigned long long blocks) } -struct PrintFreed +struct PrintFreed { bool show; const GCResults & results; PrintFreed(bool show, const GCResults & results) : show(show), results(results) { } - ~PrintFreed() + ~PrintFreed() { if (show) cout << format("%1% store paths deleted, %2% freed\n") @@ -572,9 +572,9 @@ static void opGC(Strings opFlags, Strings opArgs) bool printRoots = false; GCOptions options; options.action = GCOptions::gcDeleteDead; - + GCResults results; - + /* Do what? */ foreach (Strings::iterator, i, opFlags) if (*i == "--print-roots") printRoots = true; @@ -613,14 +613,14 @@ static void opDelete(Strings opFlags, Strings opArgs) { GCOptions options; options.action = GCOptions::gcDeleteSpecific; - + foreach (Strings::iterator, i, opFlags) if (*i == "--ignore-liveness") options.ignoreLiveness = true; else throw UsageError(format("unknown flag `%1%'") % *i); foreach (Strings::iterator, i, opArgs) options.pathsToDelete.insert(followLinksToStorePath(*i)); - + GCResults results; PrintFreed freed(true, results); store->collectGarbage(options, results); @@ -671,9 +671,9 @@ static void opImport(Strings opFlags, Strings opArgs) foreach (Strings::iterator, i, opFlags) if (*i == "--require-signature") requireSignature = true; else throw UsageError(format("unknown flag `%1%'") % *i); - + if (!opArgs.empty()) throw UsageError("no arguments expected"); - + FdSource source(STDIN_FILENO); Paths paths = store->importPaths(requireSignature, source); @@ -700,12 +700,12 @@ static void opVerify(Strings opFlags, Strings opArgs) throw UsageError("no arguments expected"); bool checkContents = false; - + for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ++i) if (*i == "--check-contents") checkContents = true; else throw UsageError(format("unknown flag `%1%'") % *i); - + ensureLocalStore().verifyStore(checkContents); } @@ -844,7 +844,7 @@ void run(Strings args) } else if (arg == "--indirect") indirectRoot = true; - else if (arg[0] == '-') { + else if (arg[0] == '-') { opFlags.push_back(arg); if (arg == "--max-freed" || arg == "--max-links" || arg == "--max-atime") { /* !!! hack */ if (i != args.end()) opFlags.push_back(*i++); diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index 09800c160..84ad68904 100644 --- a/src/nix-worker/nix-worker.cc +++ b/src/nix-worker/nix-worker.cc @@ -527,27 +527,23 @@ static void performOp(unsigned int clientVersion, } case wopSetOptions: { - keepFailed = readInt(from) != 0; - keepGoing = readInt(from) != 0; - tryFallback = readInt(from) != 0; + settings.keepFailed = readInt(from) != 0; + settings.keepGoing = readInt(from) != 0; + settings.tryFallback = readInt(from) != 0; verbosity = (Verbosity) readInt(from); - maxBuildJobs = readInt(from); - maxSilentTime = readInt(from); + settings.maxBuildJobs = readInt(from); + settings.maxSilentTime = readInt(from); if (GET_PROTOCOL_MINOR(clientVersion) >= 2) - useBuildHook = readInt(from) != 0; + settings.useBuildHook = readInt(from) != 0; if (GET_PROTOCOL_MINOR(clientVersion) >= 4) { - buildVerbosity = (Verbosity) readInt(from); + settings.buildVerbosity = (Verbosity) readInt(from); logType = (LogType) readInt(from); - printBuildTrace = readInt(from) != 0; + settings.printBuildTrace = readInt(from) != 0; } if (GET_PROTOCOL_MINOR(clientVersion) >= 6) - buildCores = readInt(from); - if (GET_PROTOCOL_MINOR(clientVersion) >= 10) { - int x = readInt(from); - Strings ss; - ss.push_back(x == 0 ? "false" : "true"); - overrideSetting("build-use-substitutes", ss); - } + settings.buildCores = readInt(from); + if (GET_PROTOCOL_MINOR(clientVersion) >= 10) + settings.useSubstitutes = readInt(from) != 0; startWork(); stopWork(); break; @@ -768,7 +764,7 @@ static void daemonLoop() if (fdSocket == -1) throw SysError("cannot create Unix domain socket"); - string socketPath = nixStateDir + DEFAULT_SOCKET_PATH; + string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH; createDirs(dirOf(socketPath)); @@ -867,10 +863,6 @@ static void daemonLoop() strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1])); } - /* Since the daemon can be long-running, the - settings may have changed. So force a reload. */ - reloadSettings(); - /* Handle the connection. */ from.fd = remote; to.fd = remote;