diff options
Diffstat (limited to 'src/libstore')
-rw-r--r-- | src/libstore/build.cc | 235 | ||||
-rw-r--r-- | src/libstore/derivations.cc | 83 | ||||
-rw-r--r-- | src/libstore/derivations.hh | 40 | ||||
-rw-r--r-- | src/libstore/download.cc | 2 | ||||
-rw-r--r-- | src/libstore/download.hh | 5 | ||||
-rw-r--r-- | src/libstore/gc.cc | 49 | ||||
-rw-r--r-- | src/libstore/local-store.cc | 16 | ||||
-rw-r--r-- | src/libstore/local-store.hh | 12 | ||||
-rw-r--r-- | src/libstore/misc.cc | 108 | ||||
-rw-r--r-- | src/libstore/misc.hh | 40 | ||||
-rw-r--r-- | src/libstore/pathlocks.cc | 6 | ||||
-rw-r--r-- | src/libstore/profiles.cc | 4 | ||||
-rw-r--r-- | src/libstore/profiles.hh | 4 | ||||
-rw-r--r-- | src/libstore/remote-store.cc | 10 | ||||
-rw-r--r-- | src/libstore/remote-store.hh | 2 | ||||
-rw-r--r-- | src/libstore/store-api.cc | 29 | ||||
-rw-r--r-- | src/libstore/store-api.hh | 67 |
17 files changed, 405 insertions, 307 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc index e00a07d3b1a0..180a558dc2e9 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -2,7 +2,6 @@ #include "references.hh" #include "pathlocks.hh" -#include "misc.hh" #include "globals.hh" #include "local-store.hh" #include "util.hh" @@ -621,11 +620,15 @@ HookInstance::HookInstance() if (dup2(builderOut.writeSide, 4) == -1) throw SysError("dupping builder's stdout/stderr"); - 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(), - (format("%1%") % settings.buildTimeout).str().c_str(), - NULL); + Strings args = { + baseNameOf(buildHook), + settings.thisSystem, + (format("%1%") % settings.maxSilentTime).str(), + (format("%1%") % settings.printBuildTrace).str(), + (format("%1%") % settings.buildTimeout).str() + }; + + execv(buildHook.c_str(), stringsToCharPtrs(args).data()); throw SysError(format("executing ‘%1%’") % buildHook); }); @@ -902,7 +905,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, const BasicDerivation & drv { this->drv = std::unique_ptr<BasicDerivation>(new BasicDerivation(drv)); state = &DerivationGoal::haveDerivation; - name = (format("building of %1%") % showPaths(outputPaths(drv))).str(); + name = (format("building of %1%") % showPaths(drv.outputPaths())).str(); trace("created"); /* Prevent the .chroot directory from being @@ -1014,7 +1017,7 @@ void DerivationGoal::loadDerivation() assert(worker.store.isValidPath(drvPath)); /* Get the derivation. */ - drv = std::unique_ptr<BasicDerivation>(new Derivation(derivationFromPath(worker.store, drvPath))); + drv = std::unique_ptr<BasicDerivation>(new Derivation(worker.store.derivationFromPath(drvPath))); haveDerivation(); } @@ -1041,10 +1044,19 @@ void DerivationGoal::haveDerivation() for (auto & i : invalidOutputs) if (pathFailed(i)) return; + /* Reject doing a hash build of anything other than a fixed-output + derivation. */ + if (buildMode == bmHash) { + if (drv->outputs.size() != 1 || + drv->outputs.find("out") == drv->outputs.end() || + drv->outputs["out"].hashAlgo == "") + throw Error(format("cannot do a hash build of non-fixed-output derivation ‘%1%’") % drvPath); + } + /* We are first going to try to create the invalid output paths through substitutes. If that doesn't work, we'll build them. */ - if (settings.useSubstitutes && substitutesAllowed(*drv)) + if (settings.useSubstitutes && drv->substitutesAllowed()) for (auto & i : invalidOutputs) addWaitee(worker.makeSubstitutionGoal(i, buildMode == bmRepair)); @@ -1123,8 +1135,10 @@ void DerivationGoal::repairClosure() /* Get the output closure. */ PathSet outputClosure; - for (auto & i : drv->outputs) - computeFSClosure(worker.store, i.second.path, outputClosure); + for (auto & i : drv->outputs) { + if (!wantOutput(i.first, wantedOutputs)) continue; + worker.store.computeFSClosure(i.second.path, outputClosure); + } /* Filter out our own outputs (which we have already checked). */ for (auto & i : drv->outputs) @@ -1134,11 +1148,11 @@ void DerivationGoal::repairClosure() derivation is responsible for which path in the output closure. */ PathSet inputClosure; - if (useDerivation) computeFSClosure(worker.store, drvPath, inputClosure); + if (useDerivation) worker.store.computeFSClosure(drvPath, inputClosure); std::map<Path, Path> outputsToDrv; for (auto & i : inputClosure) if (isDerivation(i)) { - Derivation drv = derivationFromPath(worker.store, i); + Derivation drv = worker.store.derivationFromPath(i); for (auto & j : drv.outputs) outputsToDrv[j.second.path] = i; } @@ -1210,10 +1224,10 @@ void DerivationGoal::inputsRealised() `i' as input paths. Only add the closures of output paths that are specified as inputs. */ assert(worker.store.isValidPath(i.first)); - Derivation inDrv = derivationFromPath(worker.store, i.first); + Derivation inDrv = worker.store.derivationFromPath(i.first); for (auto & j : i.second) if (inDrv.outputs.find(j) != inDrv.outputs.end()) - computeFSClosure(worker.store, inDrv.outputs[j].path, inputPaths); + worker.store.computeFSClosure(inDrv.outputs[j].path, inputPaths); else throw Error( format("derivation ‘%1%’ requires non-existent output ‘%2%’ from input derivation ‘%3%’") @@ -1222,7 +1236,7 @@ void DerivationGoal::inputsRealised() /* Second, the input sources. */ for (auto & i : drv->inputSrcs) - computeFSClosure(worker.store, i, inputPaths); + worker.store.computeFSClosure(i, inputPaths); debug(format("added input paths %1%") % showPaths(inputPaths)); @@ -1245,46 +1259,6 @@ void DerivationGoal::inputsRealised() } -static bool isBuiltin(const BasicDerivation & drv) -{ - return string(drv.builder, 0, 8) == "builtin:"; -} - - -static bool canBuildLocally(const BasicDerivation & drv) -{ - return drv.platform == settings.thisSystem - || isBuiltin(drv) -#if __linux__ - || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux") - || (drv.platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux") -#elif __FreeBSD__ - || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-freebsd") - || (drv.platform == "i686-linux" && settings.thisSystem == "i686-freebsd") -#endif - ; -} - - -static string get(const StringPairs & map, const string & key, const string & def = "") -{ - StringPairs::const_iterator i = map.find(key); - return i == map.end() ? def : i->second; -} - - -bool willBuildLocally(const BasicDerivation & drv) -{ - return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv); -} - - -bool substitutesAllowed(const BasicDerivation & drv) -{ - return get(drv.env, "allowSubstitutes", "1") == "1"; -} - - void DerivationGoal::tryToBuild() { trace("trying to build"); @@ -1307,7 +1281,7 @@ void DerivationGoal::tryToBuild() can't acquire the lock, then continue; hopefully some other goal can start a build, and if not, the main loop will sleep a few seconds and then retry this goal. */ - if (!outputLocks.lockPaths(outputPaths(*drv), "", false)) { + if (!outputLocks.lockPaths(drv->outputPaths(), "", false)) { worker.waitForAWhile(shared_from_this()); return; } @@ -1320,7 +1294,6 @@ void DerivationGoal::tryToBuild() now hold the locks on the output paths, no other process can build this derivation, so no further checks are necessary. */ validPaths = checkPathValidity(true, buildMode == bmRepair); - assert(buildMode != bmCheck || validPaths.size() == drv->outputs.size()); if (buildMode != bmCheck && validPaths.size() == drv->outputs.size()) { debug(format("skipping build of derivation ‘%1%’, someone beat us to it") % drvPath); outputLocks.setDeletion(true); @@ -1328,7 +1301,7 @@ void DerivationGoal::tryToBuild() return; } - missingPaths = outputPaths(*drv); + missingPaths = drv->outputPaths(); if (buildMode != bmCheck) for (auto & i : validPaths) missingPaths.erase(i); @@ -1351,7 +1324,7 @@ void DerivationGoal::tryToBuild() /* Don't do a remote build if the derivation has the attribute `preferLocalBuild' set. Also, check and repair modes are only supported for local builds. */ - bool buildLocally = buildMode != bmNormal || willBuildLocally(*drv); + bool buildLocally = buildMode != bmNormal || drv->willBuildLocally(); /* Is the build hook willing to accept this job? */ if (!buildLocally) { @@ -1647,7 +1620,7 @@ HookReply DerivationGoal::tryBuildHook() list it since the remote system *probably* already has it.) */ PathSet allInputs; allInputs.insert(inputPaths.begin(), inputPaths.end()); - computeFSClosure(worker.store, drvPath, allInputs); + worker.store.computeFSClosure(drvPath, allInputs); string s; for (auto & i : allInputs) { s += i; s += ' '; } @@ -1702,7 +1675,7 @@ void DerivationGoal::startBuilder() startNest(nest, lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds); /* Right platform? */ - if (!canBuildLocally(*drv)) { + if (!drv->canBuildLocally()) { if (settings.printBuildTrace) printMsg(lvlError, format("@ unsupported-platform %1% %2%") % drvPath % drv->platform); throw Error( @@ -1710,6 +1683,10 @@ void DerivationGoal::startBuilder() % drv->platform % settings.thisSystem % drvPath); } +#if __APPLE__ + additionalSandboxProfile = get(drv->env, "__sandboxProfile"); +#endif + /* Are we doing a chroot build? Note that fixed-output derivations are never done in a chroot, mainly so that functions like fetchurl (which needs a proper /etc/resolv.conf) @@ -1723,7 +1700,13 @@ void DerivationGoal::startBuilder() throw Error("option ‘build-use-sandbox’ must be set to one of ‘true’, ‘false’ or ‘relaxed’"); if (x == "true") { if (get(drv->env, "__noChroot") == "1") - throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath); + throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, " + "but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath); +#if __APPLE__ + if (additionalSandboxProfile != "") + throw Error(format("derivation ‘%1%’ specifies a sandbox profile, " + "but this is only allowed when ‘build-use-sandbox’ is ‘relaxed’") % drvPath); +#endif useChroot = true; } else if (x == "false") @@ -1849,14 +1832,14 @@ void DerivationGoal::startBuilder() like passing all build-time dependencies of some path to a derivation that builds a NixOS DVD image. */ PathSet paths, paths2; - computeFSClosure(worker.store, storePath, paths); + worker.store.computeFSClosure(storePath, paths); paths2 = paths; for (auto & j : paths2) { if (isDerivation(j)) { - Derivation drv = derivationFromPath(worker.store, j); + Derivation drv = worker.store.derivationFromPath(j); for (auto & k : drv.outputs) - computeFSClosure(worker.store, k.second.path, paths); + worker.store.computeFSClosure(k.second.path, paths); } } @@ -1920,13 +1903,10 @@ void DerivationGoal::startBuilder() PathSet closure; for (auto & i : dirsInChroot) if (isInStore(i.second)) - computeFSClosure(worker.store, toStorePath(i.second), closure); + worker.store.computeFSClosure(toStorePath(i.second), closure); for (auto & i : closure) dirsInChroot[i] = i; -#if __APPLE__ - additionalSandboxProfile = get(drv->env, "__sandboxProfile"); -#endif string allowed = settings.get("allowed-impure-host-deps", string(DEFAULT_ALLOWED_IMPURE_PREFIXES)); PathSet allowedPaths = tokenizeString<StringSet>(allowed); @@ -1948,7 +1928,7 @@ void DerivationGoal::startBuilder() } } if (!found) - throw Error(format("derivation '%1%' requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed); + throw Error(format("derivation ‘%1%’ requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed); dirsInChroot[i] = i; } @@ -2170,7 +2150,7 @@ void DerivationGoal::startBuilder() size_t stackSize = 1 * 1024 * 1024; char * stack = (char *) mmap(0, stackSize, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); - if (!stack) throw SysError("allocating stack"); + if (stack == MAP_FAILED) throw SysError("allocating stack"); int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; if (!fixedOutput) flags |= CLONE_NEWNET; pid_t child = clone(childEntry, stack + stackSize, flags, this); @@ -2191,7 +2171,7 @@ void DerivationGoal::startBuilder() #endif { ProcessOptions options; - options.allowVfork = !buildUser.enabled() && !isBuiltin(*drv); + options.allowVfork = !buildUser.enabled() && !drv->isBuiltin(); pid = startProcess([&]() { runChild(); }, options); @@ -2445,7 +2425,7 @@ void DerivationGoal::runChild() const char *builder = "invalid"; string sandboxProfile; - if (isBuiltin(*drv)) { + if (drv->isBuiltin()) { ; #if __APPLE__ } else if (useChroot) { @@ -2562,7 +2542,7 @@ void DerivationGoal::runChild() writeFull(STDERR_FILENO, string("\1\n")); /* Execute the program. This should not return. */ - if (isBuiltin(*drv)) { + if (drv->isBuiltin()) { try { logType = ltFlat; if (drv->builder == "builtin:fetchurl") @@ -2625,6 +2605,8 @@ void DerivationGoal::registerOutputs() outputs to allow hard links between outputs. */ InodesSeen inodesSeen; + Path checkSuffix = "-check"; + /* Check whether the output paths were created, and grep each output path to determine what other paths it references. Also make all output paths read-only. */ @@ -2650,7 +2632,7 @@ void DerivationGoal::registerOutputs() && redirectedBadOutputs.find(path) != redirectedBadOutputs.end() && pathExists(redirected)) replaceValidPath(path, redirected); - if (buildMode == bmCheck) + if (buildMode == bmCheck && redirected != "") actualPath = redirected; } @@ -2713,12 +2695,29 @@ void DerivationGoal::registerOutputs() format("output path ‘%1%’ should be a non-executable regular file") % path); } - /* Check the hash. */ + /* Check the hash. In hash mode, move the path produced by + the derivation to its content-addressed location. */ Hash h2 = recursive ? hashPath(ht, actualPath).first : hashFile(ht, actualPath); - if (h != h2) - throw BuildError( - format("output path ‘%1%’ has %2% hash ‘%3%’ when ‘%4%’ was expected") - % path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h)); + if (buildMode == bmHash) { + Path dest = makeFixedOutputPath(recursive, ht, h2, drv->env["name"]); + printMsg(lvlError, format("build produced path ‘%1%’ with %2% hash ‘%3%’") + % dest % printHashType(ht) % printHash16or32(h2)); + if (worker.store.isValidPath(dest)) + return; + if (actualPath != dest) { + PathLocks outputLocks({dest}); + if (pathExists(dest)) + deletePath(dest); + if (rename(actualPath.c_str(), dest.c_str()) == -1) + throw SysError(format("moving ‘%1%’ to ‘%2%’") % actualPath % dest); + } + path = actualPath = dest; + } else { + if (h != h2) + throw BuildError( + format("output path ‘%1%’ has %2% hash ‘%3%’ when ‘%4%’ was expected") + % path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h)); + } } /* Get rid of all weird permissions. This also checks that @@ -2734,9 +2733,20 @@ void DerivationGoal::registerOutputs() PathSet references = scanForReferences(actualPath, allPaths, hash); if (buildMode == bmCheck) { + if (!worker.store.isValidPath(path)) continue; ValidPathInfo info = worker.store.queryPathInfo(path); - if (hash.first != info.hash) - throw Error(format("derivation ‘%1%’ may not be deterministic: hash mismatch in output ‘%2%’") % drvPath % path); + if (hash.first != info.hash) { + if (settings.keepFailed) { + Path dst = path + checkSuffix; + if (pathExists(dst)) deletePath(dst); + if (rename(actualPath.c_str(), dst.c_str())) + throw SysError(format("renaming ‘%1%’ to ‘%2%’") % actualPath % dst); + throw Error(format("derivation ‘%1%’ may not be deterministic: output ‘%2%’ differs from ‘%3%’") + % drvPath % path % dst); + } else + throw Error(format("derivation ‘%1%’ may not be deterministic: output ‘%2%’ differs") + % drvPath % path); + } continue; } @@ -2762,7 +2772,7 @@ void DerivationGoal::registerOutputs() for (auto & i : references) /* Don't call computeFSClosure on ourselves. */ if (actualPath != i) - computeFSClosure(worker.store, i, used); + worker.store.computeFSClosure(i, used); } else used = references; @@ -2781,9 +2791,11 @@ void DerivationGoal::registerOutputs() checkRefs("disallowedReferences", false, false); checkRefs("disallowedRequisites", false, true); - worker.store.optimisePath(path); // FIXME: combine with scanForReferences() + if (curRound == nrRounds) { + worker.store.optimisePath(path); // FIXME: combine with scanForReferences() - worker.store.markContentsGood(path); + worker.store.markContentsGood(path); + } ValidPathInfo info; info.path = path; @@ -2796,10 +2808,37 @@ void DerivationGoal::registerOutputs() if (buildMode == bmCheck) return; - if (curRound > 1 && prevInfos != infos) - throw NotDeterministic( - format("result of ‘%1%’ differs from previous round; rejecting as non-deterministic") - % drvPath); + /* Compare the result with the previous round, and report which + path is different, if any.*/ + if (curRound > 1 && prevInfos != infos) { + assert(prevInfos.size() == infos.size()); + for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j) + if (!(*i == *j)) { + Path prev = i->path + checkSuffix; + if (pathExists(prev)) + throw NotDeterministic( + format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round") + % i->path % drvPath % prev); + else + throw NotDeterministic( + format("output ‘%1%’ of ‘%2%’ differs from previous round") + % i->path % drvPath); + } + assert(false); // shouldn't happen + } + + if (settings.keepFailed) { + for (auto & i : drv->outputs) { + Path prev = i.second.path + checkSuffix; + if (pathExists(prev)) deletePath(prev); + if (curRound < nrRounds) { + Path dst = i.second.path + checkSuffix; + if (rename(i.second.path.c_str(), dst.c_str())) + throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst); + } + } + + } if (curRound < nrRounds) { prevInfos = infos; @@ -3122,6 +3161,7 @@ void SubstitutionGoal::tryNext() /* None left. Terminate this goal and let someone else deal with it. */ debug(format("path ‘%1%’ is required, but there is no substituter that can build it") % storePath); + /* Hack: don't indicate failure if there were no substituters. In that case the calling derivation should just do a build. */ @@ -3721,7 +3761,7 @@ void Worker::waitForInput() } } - if (!waitingForAWhile.empty() && lastWokenUp + settings.pollInterval <= after) { + if (!waitingForAWhile.empty() && lastWokenUp + (time_t) settings.pollInterval <= after) { lastWokenUp = after; for (auto & i : waitingForAWhile) { GoalPtr goal = i.lock(); @@ -3817,8 +3857,17 @@ void LocalStore::repairPath(const Path & path) worker.run(goals); - if (goal->getExitCode() != Goal::ecSuccess) - throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus()); + if (goal->getExitCode() != Goal::ecSuccess) { + /* Since substituting the path didn't work, if we have a valid + deriver, then rebuild the deriver. */ + Path deriver = queryDeriver(path); + if (deriver != "" && isValidPath(deriver)) { + goals.clear(); + goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair)); + worker.run(goals); + } else + throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus()); + } } diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 7959d5bfc3d5..d9b009d40322 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -2,7 +2,6 @@ #include "store-api.hh" #include "globals.hh" #include "util.hh" -#include "misc.hh" #include "worker-protocol.hh" @@ -27,7 +26,49 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash } -Path writeDerivation(StoreAPI & store, +Path BasicDerivation::findOutput(const string & id) const +{ + auto i = outputs.find(id); + if (i == outputs.end()) + throw Error(format("derivation has no output ‘%1%’") % id); + return i->second.path; +} + + +bool BasicDerivation::willBuildLocally() const +{ + return get(env, "preferLocalBuild") == "1" && canBuildLocally(); +} + + +bool BasicDerivation::substitutesAllowed() const +{ + return get(env, "allowSubstitutes", "1") == "1"; +} + + +bool BasicDerivation::isBuiltin() const +{ + return string(builder, 0, 8) == "builtin:"; +} + + +bool BasicDerivation::canBuildLocally() const +{ + return platform == settings.thisSystem + || isBuiltin() +#if __linux__ + || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux") + || (platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux") +#elif __FreeBSD__ + || (platform == "i686-linux" && settings.thisSystem == "x86_64-freebsd") + || (platform == "i686-linux" && settings.thisSystem == "i686-freebsd") +#endif + ; +} + + +Path writeDerivation(ref<Store> store, const Derivation & drv, const string & name, bool repair) { PathSet references; @@ -38,10 +79,10 @@ Path writeDerivation(StoreAPI & store, (that can be missing (of course) and should not necessarily be held during a garbage collection). */ string suffix = name + drvExtension; - string contents = unparseDerivation(drv); + string contents = drv.unparse(); return settings.readOnlyMode ? computeStorePathForText(suffix, contents, references) - : store.addTextToStore(suffix, contents, references, repair); + : store->addTextToStore(suffix, contents, references, repair); } @@ -149,14 +190,14 @@ static void printStrings(string & res, ForwardIterator i, ForwardIterator j) } -string unparseDerivation(const Derivation & drv) +string Derivation::unparse() const { string s; s.reserve(65536); s += "Derive(["; bool first = true; - for (auto & i : drv.outputs) { + for (auto & i : outputs) { if (first) first = false; else s += ','; s += '('; printString(s, i.first); s += ','; printString(s, i.second.path); @@ -167,7 +208,7 @@ string unparseDerivation(const Derivation & drv) s += "],["; first = true; - for (auto & i : drv.inputDrvs) { + for (auto & i : inputDrvs) { if (first) first = false; else s += ','; s += '('; printString(s, i.first); s += ','; printStrings(s, i.second.begin(), i.second.end()); @@ -175,15 +216,15 @@ string unparseDerivation(const Derivation & drv) } s += "],"; - printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end()); + printStrings(s, inputSrcs.begin(), inputSrcs.end()); - s += ','; printString(s, drv.platform); - s += ','; printString(s, drv.builder); - s += ','; printStrings(s, drv.args.begin(), drv.args.end()); + s += ','; printString(s, platform); + s += ','; printString(s, builder); + s += ','; printStrings(s, args.begin(), args.end()); s += ",["; first = true; - for (auto & i : drv.env) { + for (auto & i : env) { if (first) first = false; else s += ','; s += '('; printString(s, i.first); s += ','; printString(s, i.second); @@ -202,11 +243,11 @@ bool isDerivation(const string & fileName) } -bool isFixedOutputDrv(const Derivation & drv) +bool BasicDerivation::isFixedOutput() const { - return drv.outputs.size() == 1 && - drv.outputs.begin()->first == "out" && - drv.outputs.begin()->second.hash != ""; + return outputs.size() == 1 && + outputs.begin()->first == "out" && + outputs.begin()->second.hash != ""; } @@ -233,10 +274,10 @@ DrvHashes drvHashes; paths have been replaced by the result of a recursive call to this function, and that for fixed-output derivations we return a hash of its output path. */ -Hash hashDerivationModulo(StoreAPI & store, Derivation drv) +Hash hashDerivationModulo(Store & store, Derivation drv) { /* Return a fixed hash for fixed-output derivations. */ - if (isFixedOutputDrv(drv)) { + if (drv.isFixedOutput()) { DerivationOutputs::const_iterator i = drv.outputs.begin(); return hashString(htSHA256, "fixed:out:" + i->second.hashAlgo + ":" @@ -259,7 +300,7 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv) } drv.inputDrvs = inputs2; - return hashString(htSHA256, unparseDerivation(drv)); + return hashString(htSHA256, drv.unparse()); } @@ -286,10 +327,10 @@ bool wantOutput(const string & output, const std::set<string> & wanted) } -PathSet outputPaths(const BasicDerivation & drv) +PathSet BasicDerivation::outputPaths() const { PathSet paths; - for (auto & i : drv.outputs) + for (auto & i : outputs) paths.insert(i.second.path); return paths; } diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index f0842045f86b..6f98869b0fe0 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -50,40 +50,56 @@ struct BasicDerivation StringPairs env; virtual ~BasicDerivation() { }; + + /* Return the path corresponding to the output identifier `id' in + the given derivation. */ + Path findOutput(const string & id) const; + + bool willBuildLocally() const; + + bool substitutesAllowed() const; + + bool isBuiltin() const; + + bool canBuildLocally() const; + + /* Return true iff this is a fixed-output derivation. */ + bool isFixedOutput() const; + + /* Return the output paths of a derivation. */ + PathSet outputPaths() const; + }; struct Derivation : BasicDerivation { DerivationInputs inputDrvs; /* inputs that are sub-derivations */ + + /* Print a derivation. */ + std::string unparse() const; }; -class StoreAPI; +class Store; /* Write a derivation to the Nix store, and return its path. */ -Path writeDerivation(StoreAPI & store, +Path writeDerivation(ref<Store> store, const Derivation & drv, const string & name, bool repair = false); /* Read a derivation from a file. */ Derivation readDerivation(const Path & drvPath); -/* Print a derivation. */ -string unparseDerivation(const Derivation & drv); - -/* Check whether a file name ends with the extensions for +/* Check whether a file name ends with the extension for derivations. */ bool isDerivation(const string & fileName); -/* Return true iff this is a fixed-output derivation. */ -bool isFixedOutputDrv(const Derivation & drv); - -Hash hashDerivationModulo(StoreAPI & store, Derivation drv); +Hash hashDerivationModulo(Store & store, Derivation drv); /* Memoisation of hashDerivationModulo(). */ typedef std::map<Path, Hash> DrvHashes; -extern DrvHashes drvHashes; +extern DrvHashes drvHashes; // FIXME: global, not thread-safe /* Split a string specifying a derivation and a set of outputs (/nix/store/hash-foo!out1,out2,...) into the derivation path and @@ -95,8 +111,6 @@ Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outpu bool wantOutput(const string & output, const std::set<string> & wanted); -PathSet outputPaths(const BasicDerivation & drv); - struct Source; struct Sink; diff --git a/src/libstore/download.cc b/src/libstore/download.cc index 822e9a8db867..01ce1ea2fd4c 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -188,7 +188,7 @@ DownloadResult downloadFile(string url, const DownloadOptions & options) } -Path downloadFileCached(const string & url, bool unpack) +Path downloadFileCached(ref<Store> store, const string & url, bool unpack) { Path cacheDir = getEnv("XDG_CACHE_HOME", getEnv("HOME", "") + "/.cache") + "/nix/tarballs"; createDirs(cacheDir); diff --git a/src/libstore/download.hh b/src/libstore/download.hh index c1cb25b90c32..7aec8de73e48 100644 --- a/src/libstore/download.hh +++ b/src/libstore/download.hh @@ -1,6 +1,7 @@ #pragma once #include "types.hh" + #include <string> namespace nix { @@ -18,9 +19,11 @@ struct DownloadResult string data, etag; }; +class Store; + DownloadResult downloadFile(string url, const DownloadOptions & options); -Path downloadFileCached(const string & url, bool unpack); +Path downloadFileCached(ref<Store> store, const string & url, bool unpack); MakeError(DownloadError, Error) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 998a7516a13d..d19af1cefaf2 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -1,5 +1,5 @@ +#include "derivations.hh" #include "globals.hh" -#include "misc.hh" #include "local-store.hh" #include <functional> @@ -83,7 +83,7 @@ void LocalStore::addIndirectRoot(const Path & path) } -Path addPermRoot(StoreAPI & store, const Path & _storePath, +Path Store::addPermRoot(const Path & _storePath, const Path & _gcRoot, bool indirect, bool allowOutsideRootsDir) { Path storePath(canonPath(_storePath)); @@ -101,7 +101,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) throw Error(format("cannot create symlink ‘%1%’; already exists") % gcRoot); makeSymlink(gcRoot, storePath); - store.addIndirectRoot(gcRoot); + addIndirectRoot(gcRoot); } else { @@ -127,7 +127,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, check if the root is in a directory in or linked from the gcroots directory. */ if (settings.checkRootReachability) { - Roots roots = store.findRoots(); + Roots roots = findRoots(); if (roots.find(gcRoot) == roots.end()) printMsg(lvlError, format( @@ -139,7 +139,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, /* Grab the global GC root, causing us to block while a GC is in progress. This prevents the set of permanent roots from increasing while a GC is in progress. */ - store.syncWithGC(); + syncWithGC(); return gcRoot; } @@ -260,19 +260,16 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) } -static void foundRoot(StoreAPI & store, - const Path & path, const Path & target, Roots & roots) +void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) { - Path storePath = toStorePath(target); - if (store.isValidPath(storePath)) - roots[path] = storePath; - else - printMsg(lvlInfo, format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath); -} - + auto foundRoot = [&](const Path & path, const Path & target) { + Path storePath = toStorePath(target); + if (isValidPath(storePath)) + roots[path] = storePath; + else + printMsg(lvlInfo, format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath); + }; -static void findRoots(StoreAPI & store, const Path & path, unsigned char type, Roots & roots) -{ try { if (type == DT_UNKNOWN) @@ -280,13 +277,13 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R if (type == DT_DIR) { for (auto & i : readDirectory(path)) - findRoots(store, path + "/" + i.name, i.type, roots); + findRoots(path + "/" + i.name, i.type, roots); } else if (type == DT_LNK) { Path target = readLink(path); if (isInStore(target)) - foundRoot(store, path, target, roots); + foundRoot(path, target); /* Handle indirect roots. */ else { @@ -300,14 +297,14 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R struct stat st2 = lstat(target); if (!S_ISLNK(st2.st_mode)) return; Path target2 = readLink(target); - if (isInStore(target2)) foundRoot(store, target, target2, roots); + if (isInStore(target2)) foundRoot(target, target2); } } } else if (type == DT_REG) { Path storePath = settings.nixStore + "/" + baseNameOf(path); - if (store.isValidPath(storePath)) + if (isValidPath(storePath)) roots[path] = storePath; } @@ -328,16 +325,16 @@ Roots LocalStore::findRoots() Roots roots; /* Process direct roots in {gcroots,manifests,profiles}. */ - nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots); + findRoots(settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots); if (pathExists(settings.nixStateDir + "/manifests")) - nix::findRoots(*this, settings.nixStateDir + "/manifests", DT_UNKNOWN, roots); - nix::findRoots(*this, settings.nixStateDir + "/profiles", DT_UNKNOWN, roots); + findRoots(settings.nixStateDir + "/manifests", DT_UNKNOWN, roots); + findRoots(settings.nixStateDir + "/profiles", DT_UNKNOWN, roots); return roots; } -static void addAdditionalRoots(StoreAPI & store, PathSet & roots) +void LocalStore::findRuntimeRoots(PathSet & roots) { Path rootFinder = getEnv("NIX_ROOT_FINDER", settings.nixLibexecDir + "/nix/find-runtime-roots.pl"); @@ -353,7 +350,7 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots) for (auto & i : paths) if (isInStore(i)) { Path path = toStorePath(i); - if (roots.find(path) == roots.end() && store.isValidPath(path)) { + if (roots.find(path) == roots.end() && isValidPath(path)) { debug(format("got additional root ‘%1%’") % path); roots.insert(path); } @@ -628,7 +625,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) to add running programs to the set of roots (to prevent them from being garbage collected). */ if (!options.ignoreLiveness) - addAdditionalRoots(*this, state.roots); + findRuntimeRoots(state.roots); /* Read the temporary roots. This acquires read locks on all per-process temporary root files. So after this point no paths diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index d7cd0b088d98..13179459f1c1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -397,9 +397,15 @@ int LocalStore::getSchema() } +bool LocalStore::haveWriteAccess() +{ + return access(settings.nixDBPath.c_str(), R_OK | W_OK) == 0; +} + + void LocalStore::openDB(bool create) { - if (access(settings.nixDBPath.c_str(), R_OK | W_OK)) + if (!haveWriteAccess()) throw SysError(format("Nix database directory ‘%1%’ is not writable") % settings.nixDBPath); /* Open the Nix database. */ @@ -602,10 +608,10 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe users group); we check for this case below. */ if (st.st_uid != geteuid()) { #if HAVE_LCHOWN - if (lchown(path.c_str(), geteuid(), (gid_t) -1) == -1) + if (lchown(path.c_str(), geteuid(), getegid()) == -1) #else if (!S_ISLNK(st.st_mode) && - chown(path.c_str(), geteuid(), (gid_t) -1) == -1) + chown(path.c_str(), geteuid(), getegid()) == -1) #endif throw SysError(format("changing owner of ‘%1%’ to %2%") % path % geteuid()); @@ -649,7 +655,7 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation & assert(isDerivation(drvName)); drvName = string(drvName, 0, drvName.size() - drvExtension.size()); - if (isFixedOutputDrv(drv)) { + if (drv.isFixedOutput()) { DerivationOutputs::const_iterator out = drv.outputs.find("out"); if (out == drv.outputs.end()) throw Error(format("derivation ‘%1%’ does not have an output named ‘out’") % drvPath); @@ -1329,7 +1335,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos) error if a cycle is detected and roll back the transaction. Cycles can only occur when a derivation has multiple outputs. */ - topoSortPaths(*this, paths); + topoSortPaths(paths); txn.commit(); } end_retry_sqlite; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index ebdf19bf1359..b6d39d345c94 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -80,7 +80,7 @@ struct SQLiteStmt }; -class LocalStore : public StoreAPI +class LocalStore : public Store { private: typedef std::map<Path, RunningSubstituter> RunningSubstituters; @@ -253,6 +253,12 @@ private: int getSchema(); +public: + + static bool haveWriteAccess(); + +private: + void openDB(bool create); void makeStoreWritable(); @@ -297,6 +303,10 @@ private: int openGCLock(LockType lockType); + void findRoots(const Path & path, unsigned char type, Roots & roots); + + void findRuntimeRoots(PathSet & roots); + void removeUnusedLinks(const GCState & state); void startSubstituter(const Path & substituter, diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 61a976c02f5c..12472f017ce4 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -1,21 +1,21 @@ -#include "misc.hh" -#include "store-api.hh" -#include "local-store.hh" +#include "derivations.hh" #include "globals.hh" +#include "local-store.hh" +#include "store-api.hh" namespace nix { -Derivation derivationFromPath(StoreAPI & store, const Path & drvPath) +Derivation Store::derivationFromPath(const Path & drvPath) { assertStorePath(drvPath); - store.ensurePath(drvPath); + ensurePath(drvPath); return readDerivation(drvPath); } -void computeFSClosure(StoreAPI & store, const Path & path, +void Store::computeFSClosure(const Path & path, PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers) { if (paths.find(path) != paths.end()) return; @@ -24,50 +24,42 @@ void computeFSClosure(StoreAPI & store, const Path & path, PathSet edges; if (flipDirection) { - store.queryReferrers(path, edges); + queryReferrers(path, edges); if (includeOutputs) { - PathSet derivers = store.queryValidDerivers(path); + PathSet derivers = queryValidDerivers(path); for (auto & i : derivers) edges.insert(i); } if (includeDerivers && isDerivation(path)) { - PathSet outputs = store.queryDerivationOutputs(path); + PathSet outputs = queryDerivationOutputs(path); for (auto & i : outputs) - if (store.isValidPath(i) && store.queryDeriver(i) == path) + if (isValidPath(i) && queryDeriver(i) == path) edges.insert(i); } } else { - store.queryReferences(path, edges); + queryReferences(path, edges); if (includeOutputs && isDerivation(path)) { - PathSet outputs = store.queryDerivationOutputs(path); + PathSet outputs = queryDerivationOutputs(path); for (auto & i : outputs) - if (store.isValidPath(i)) edges.insert(i); + if (isValidPath(i)) edges.insert(i); } if (includeDerivers) { - Path deriver = store.queryDeriver(path); - if (store.isValidPath(deriver)) edges.insert(deriver); + Path deriver = queryDeriver(path); + if (isValidPath(deriver)) edges.insert(deriver); } } for (auto & i : edges) - computeFSClosure(store, i, paths, flipDirection, includeOutputs, includeDerivers); -} - - -Path findOutput(const Derivation & drv, string id) -{ - for (auto & i : drv.outputs) - if (i.first == id) return i.second.path; - throw Error(format("derivation has no output ‘%1%’") % id); + computeFSClosure(i, paths, flipDirection, includeOutputs, includeDerivers); } -void queryMissing(StoreAPI & store, const PathSet & targets, +void Store::queryMissing(const PathSet & targets, PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown, unsigned long long & downloadSize, unsigned long long & narSize) { @@ -105,27 +97,27 @@ void queryMissing(StoreAPI & store, const PathSet & targets, DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i); if (isDerivation(i2.first)) { - if (!store.isValidPath(i2.first)) { + if (!isValidPath(i2.first)) { // FIXME: we could try to substitute p. unknown.insert(i); continue; } - Derivation drv = derivationFromPath(store, i2.first); + Derivation drv = derivationFromPath(i2.first); PathSet invalid; for (auto & j : drv.outputs) if (wantOutput(j.first, i2.second) - && !store.isValidPath(j.second.path)) + && !isValidPath(j.second.path)) invalid.insert(j.second.path); if (invalid.empty()) continue; todoDrv.insert(i); - if (settings.useSubstitutes && substitutesAllowed(drv)) + if (settings.useSubstitutes && drv.substitutesAllowed()) query.insert(invalid.begin(), invalid.end()); } else { - if (store.isValidPath(i)) continue; + if (isValidPath(i)) continue; query.insert(i); todoNonDrv.insert(i); } @@ -134,20 +126,20 @@ void queryMissing(StoreAPI & store, const PathSet & targets, todo.clear(); SubstitutablePathInfos infos; - store.querySubstitutablePathInfos(query, infos); + querySubstitutablePathInfos(query, infos); for (auto & i : todoDrv) { DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i); // FIXME: cache this - Derivation drv = derivationFromPath(store, i2.first); + Derivation drv = derivationFromPath(i2.first); PathSet outputs; bool mustBuild = false; - if (settings.useSubstitutes && substitutesAllowed(drv)) { + if (settings.useSubstitutes && drv.substitutesAllowed()) { for (auto & j : drv.outputs) { if (!wantOutput(j.first, i2.second)) continue; - if (!store.isValidPath(j.second.path)) { + if (!isValidPath(j.second.path)) { if (infos.find(j.second.path) == infos.end()) mustBuild = true; else @@ -181,38 +173,38 @@ void queryMissing(StoreAPI & store, const PathSet & targets, } -static void dfsVisit(StoreAPI & store, const PathSet & paths, - const Path & path, PathSet & visited, Paths & sorted, - PathSet & parents) +Paths Store::topoSortPaths(const PathSet & paths) { - if (parents.find(path) != parents.end()) - throw BuildError(format("cycle detected in the references of ‘%1%’") % path); + Paths sorted; + PathSet visited, parents; - if (visited.find(path) != visited.end()) return; - visited.insert(path); - parents.insert(path); + std::function<void(const Path & path)> dfsVisit; - PathSet references; - if (store.isValidPath(path)) - store.queryReferences(path, references); + dfsVisit = [&](const Path & path) { + if (parents.find(path) != parents.end()) + throw BuildError(format("cycle detected in the references of ‘%1%’") % path); - for (auto & i : references) - /* Don't traverse into paths that don't exist. That can - happen due to substitutes for non-existent paths. */ - if (i != path && paths.find(i) != paths.end()) - dfsVisit(store, paths, i, visited, sorted, parents); + if (visited.find(path) != visited.end()) return; + visited.insert(path); + parents.insert(path); - sorted.push_front(path); - parents.erase(path); -} + PathSet references; + if (isValidPath(path)) + queryReferences(path, references); + for (auto & i : references) + /* Don't traverse into paths that don't exist. That can + happen due to substitutes for non-existent paths. */ + if (i != path && paths.find(i) != paths.end()) + dfsVisit(i); + + sorted.push_front(path); + parents.erase(path); + }; -Paths topoSortPaths(StoreAPI & store, const PathSet & paths) -{ - Paths sorted; - PathSet visited, parents; for (auto & i : paths) - dfsVisit(store, paths, i, visited, sorted, parents); + dfsVisit(i); + return sorted; } diff --git a/src/libstore/misc.hh b/src/libstore/misc.hh deleted file mode 100644 index 495c528750fb..000000000000 --- a/src/libstore/misc.hh +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "derivations.hh" - - -namespace nix { - - -/* Read a derivation, after ensuring its existence through - ensurePath(). */ -Derivation derivationFromPath(StoreAPI & store, const Path & drvPath); - -/* Place in `paths' the set of all store paths in the file system - closure of `storePath'; that is, all paths than can be directly or - indirectly reached from it. `paths' is not cleared. If - `flipDirection' is true, the set of paths that can reach - `storePath' is returned; that is, the closures under the - `referrers' relation instead of the `references' relation is - returned. */ -void computeFSClosure(StoreAPI & store, const Path & path, - PathSet & paths, bool flipDirection = false, - bool includeOutputs = false, bool includeDerivers = false); - -/* Return the path corresponding to the output identifier `id' in the - given derivation. */ -Path findOutput(const Derivation & drv, string id); - -/* Given a set of paths that are to be built, return the set of - derivations that will be built, and the set of output paths that - will be substituted. */ -void queryMissing(StoreAPI & store, const PathSet & targets, - PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown, - unsigned long long & downloadSize, unsigned long long & narSize); - -bool willBuildLocally(const BasicDerivation & drv); - -bool substitutesAllowed(const BasicDerivation & drv); - - -} diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc index 1c87034f8e1a..eddf5bcbda65 100644 --- a/src/libstore/pathlocks.cc +++ b/src/libstore/pathlocks.cc @@ -161,7 +161,11 @@ bool PathLocks::lockPaths(const PathSet & _paths, PathLocks::~PathLocks() { - unlock(); + try { + unlock(); + } catch (...) { + ignoreException(); + } } diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index da3f7da9d19d..cc83a838eddc 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -74,7 +74,7 @@ static void makeName(const Path & profile, unsigned int num, } -Path createGeneration(Path profile, Path outPath) +Path createGeneration(ref<Store> store, Path profile, Path outPath) { /* The new generation number should be higher than old the previous ones. */ @@ -108,7 +108,7 @@ Path createGeneration(Path profile, Path outPath) user environment etc. we've just built. */ Path generation; makeName(profile, num + 1, generation); - addPermRoot(*store, outPath, generation, false, true); + store->addPermRoot(outPath, generation, false, true); return generation; } diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index e99bbf398a86..d758d94b603c 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -31,7 +31,9 @@ typedef list<Generation> Generations; profile, sorted by generation number. */ Generations findGenerations(Path profile, int & curGen); -Path createGeneration(Path profile, Path outPath); +class Store; + +Path createGeneration(ref<Store> store, Path profile, Path outPath); void deleteGeneration(const Path & profile, unsigned int gen); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 262e4650bfb5..679210d4cb16 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -50,14 +50,8 @@ void RemoteStore::openConnection(bool reserveSpace) if (initialised) return; initialised = true; - string remoteMode = getEnv("NIX_REMOTE"); - - if (remoteMode == "daemon") - /* Connect to a daemon that does the privileged work for - us. */ - connectToDaemon(); - else - throw Error(format("invalid setting for NIX_REMOTE, ‘%1%’") % remoteMode); + /* Connect to a daemon that does the privileged work for us. */ + connectToDaemon(); from.fd = fdSocket; to.fd = fdSocket; diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index b0787c072904..f15182285e8c 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -14,7 +14,7 @@ struct FdSink; struct FdSource; -class RemoteStore : public StoreAPI +class RemoteStore : public Store { public: diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index a73ebd824264..039d07e29b3e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -220,7 +220,7 @@ Path computeStorePathForText(const string & name, const string & s, /* Return a string accepted by decodeValidPathInfo() that registers the specified paths as valid. Note: it's the responsibility of the caller to provide a closure. */ -string StoreAPI::makeValidityRegistration(const PathSet & paths, +string Store::makeValidityRegistration(const PathSet & paths, bool showDerivers, bool showHash) { string s = ""; @@ -284,12 +284,12 @@ string showPaths(const PathSet & paths) } -void exportPaths(StoreAPI & store, const Paths & paths, +void Store::exportPaths(const Paths & paths, bool sign, Sink & sink) { for (auto & i : paths) { sink << 1; - store.exportPath(i, sign, sink); + exportPath(i, sign, sink); } sink << 0; } @@ -306,15 +306,24 @@ void exportPaths(StoreAPI & store, const Paths & paths, namespace nix { -std::shared_ptr<StoreAPI> store; +ref<Store> openStore(bool reserveSpace) +{ + enum { mDaemon, mLocal, mAuto } mode; + mode = getEnv("NIX_REMOTE") == "daemon" ? mDaemon : mAuto; -std::shared_ptr<StoreAPI> openStore(bool reserveSpace) -{ - if (getEnv("NIX_REMOTE") == "") - return std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace)); - else - return std::shared_ptr<StoreAPI>(new RemoteStore()); + if (mode == mAuto) { + if (LocalStore::haveWriteAccess()) + mode = mLocal; + else if (pathExists(settings.nixDaemonSocketFile)) + mode = mDaemon; + else + mode = mLocal; + } + + return mode == mDaemon + ? (ref<Store>) make_ref<RemoteStore>() + : (ref<Store>) make_ref<LocalStore>(reserveSpace); } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 9cc5fd45b7c4..888ef3e2a083 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -103,7 +103,7 @@ struct ValidPathInfo typedef list<ValidPathInfo> ValidPathInfos; -enum BuildMode { bmNormal, bmRepair, bmCheck }; +enum BuildMode { bmNormal, bmRepair, bmCheck, bmHash }; struct BuildResult @@ -132,13 +132,14 @@ struct BuildResult struct BasicDerivation; +struct Derivation; -class StoreAPI +class Store { public: - virtual ~StoreAPI() { } + virtual ~Store() { } /* Check whether a path is valid. */ virtual bool isValidPath(const Path & path) = 0; @@ -214,6 +215,10 @@ public: virtual void exportPath(const Path & path, bool sign, Sink & sink) = 0; + /* Export multiple paths in the format expected by ‘nix-store + --import’. */ + void exportPaths(const Paths & paths, bool sign, Sink & sink); + /* Import a sequence of NAR dumps created by exportPaths() into the Nix store. */ virtual Paths importPaths(bool requireSignature, Source & source) = 0; @@ -250,6 +255,10 @@ public: `path' has disappeared. */ virtual void addIndirectRoot(const Path & path) = 0; + /* Register a permanent GC root. */ + Path addPermRoot(const Path & storePath, + const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false); + /* Acquire the global GC lock, then immediately release it. This function must be called after registering a new permanent root, but before exiting. Otherwise, it is possible that a running @@ -298,6 +307,35 @@ public: /* Check the integrity of the Nix store. Returns true if errors remain. */ virtual bool verifyStore(bool checkContents, bool repair) = 0; + + /* Utility functions. */ + + /* Read a derivation, after ensuring its existence through + ensurePath(). */ + Derivation derivationFromPath(const Path & drvPath); + + /* Place in `paths' the set of all store paths in the file system + closure of `storePath'; that is, all paths than can be directly + or indirectly reached from it. `paths' is not cleared. If + `flipDirection' is true, the set of paths that can reach + `storePath' is returned; that is, the closures under the + `referrers' relation instead of the `references' relation is + returned. */ + void computeFSClosure(const Path & path, + PathSet & paths, bool flipDirection = false, + bool includeOutputs = false, bool includeDerivers = false); + + /* Given a set of paths that are to be built, return the set of + derivations that will be built, and the set of output paths + that will be substituted. */ + void queryMissing(const PathSet & targets, + PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown, + unsigned long long & downloadSize, unsigned long long & narSize); + + /* Sort a set of paths topologically under the references + relation. If p refers to q, then p preceeds q in this list. */ + Paths topoSortPaths(const PathSet & paths); + }; @@ -372,24 +410,9 @@ Path computeStorePathForText(const string & name, const string & s, void removeTempRoots(); -/* Register a permanent GC root. */ -Path addPermRoot(StoreAPI & store, const Path & storePath, - const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false); - - -/* Sort a set of paths topologically under the references relation. - If p refers to q, then p preceeds q in this list. */ -Paths topoSortPaths(StoreAPI & store, const PathSet & paths); - - -/* For now, there is a single global store API object, but we'll - purify that in the future. */ -extern std::shared_ptr<StoreAPI> store; - - /* Factory method: open the Nix database, either through the local or remote implementation. */ -std::shared_ptr<StoreAPI> openStore(bool reserveSpace = true); +ref<Store> openStore(bool reserveSpace = true); /* Display a set of paths in human-readable form (i.e., between quotes @@ -401,12 +424,6 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven = false); -/* Export multiple paths in the format expected by ‘nix-store - --import’. */ -void exportPaths(StoreAPI & store, const Paths & paths, - bool sign, Sink & sink); - - MakeError(SubstError, Error) MakeError(BuildError, Error) /* denotes a permanent build failure */ |