From d3388d3d818ad154184419d1bf5fcb5b9fd2237f Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Thu, 12 Feb 2026 13:36:43 -0500 Subject: [PATCH] libstore: extract `WorkerSettings` from `Settings` This commit moves `pollInterval`, `maxSubstitutionJobs`, `postBuildHook`, and `logLines` into a dedicated `WorkerSettings` struct that `Settings` privately inherits from, as they are only used by the build worker subsystem. This follows the same pattern as `LocalSettings` and `LogFileSettings`. --- src/libfetchers/cache.cc | 12 +- src/libfetchers/git.cc | 14 +- .../include/nix/fetchers/fetch-settings.hh | 19 + src/libmain/shared.cc | 4 +- src/libstore-test-support/test-main.cc | 4 +- .../derivation-advanced-attrs.cc | 9 +- src/libstore-tests/nix_api_store.cc | 12 +- .../build/derivation-building-goal.cc | 47 +- src/libstore/build/derivation-goal.cc | 4 +- src/libstore/build/goal.cc | 2 +- src/libstore/build/substitution-goal.cc | 4 +- src/libstore/build/worker.cc | 5 +- src/libstore/daemon.cc | 21 +- src/libstore/derivation-options.cc | 6 +- src/libstore/http-binary-cache-store.cc | 2 +- .../include/nix/store/build/worker.hh | 2 + .../include/nix/store/derivation-options.hh | 3 +- .../include/nix/store/global-paths.hh | 33 ++ src/libstore/include/nix/store/globals.hh | 435 +----------------- .../include/nix/store/local-settings.hh | 23 + src/libstore/include/nix/store/meson.build | 2 + .../include/nix/store/worker-settings.hh | 366 +++++++++++++++ src/libstore/legacy-ssh-store.cc | 6 +- src/libstore/machines.cc | 2 +- src/libstore/misc.cc | 6 +- src/libstore/remote-store.cc | 23 +- src/libstore/store-api.cc | 8 +- src/libstore/store-registration.cc | 2 +- src/libstore/unix/build/derivation-builder.cc | 4 +- src/libstore/unix/build/hook-instance.cc | 8 +- .../include/nix/store/build/hook-instance.hh | 2 +- src/nix/build-remote/build-remote.cc | 6 +- src/nix/flake.cc | 8 +- src/nix/main.cc | 14 +- src/nix/nix-build/nix-build.cc | 5 +- src/nix/nix-store/nix-store.cc | 8 +- 36 files changed, 605 insertions(+), 526 deletions(-) create mode 100644 src/libstore/include/nix/store/global-paths.hh create mode 100644 src/libstore/include/nix/store/worker-settings.hh diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index a302d77e1..79e9f220c 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -34,14 +34,20 @@ struct CacheImpl : Cache Sync _state; - CacheImpl() + /** + * This is a back-reference to the `Settings` that owns us. + */ + const Settings & settings; + + CacheImpl(const Settings & _settings) + : settings(_settings) { auto state(_state.lock()); auto dbPath = (getCacheDir() / "fetcher-cache-v4.sqlite").string(); createDirs(dirOf(dbPath)); - state->db = SQLite(dbPath, {.useWAL = settings.useSQLiteWAL}); + state->db = SQLite(dbPath, {.useWAL = nix::settings.useSQLiteWAL}); state->db.isCache(); state->db.exec(schema); @@ -154,7 +160,7 @@ ref Settings::getCache() const { auto cache(_cache.lock()); if (!*cache) - *cache = std::make_shared(); + *cache = std::make_shared(*this); return ref(*cache); } diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index a0ac1906d..44269cb17 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -37,7 +37,7 @@ namespace { // old version of git, which will ignore unrecognized `-c` options. const std::string gitInitialBranch = "__nix_dummy_branch"; -bool isCacheFileWithinTtl(time_t now, const PosixStat & st) +static bool isCacheFileWithinTtl(const Settings & settings, time_t now, const PosixStat & st) { return st.st_mtime + static_cast(settings.tarballTtl) > now; } @@ -105,7 +105,7 @@ bool storeCachedHead(const std::string & actualUrl, bool shallow, const std::str return true; } -std::optional readHeadCached(const std::string & actualUrl, bool shallow) +static std::optional readHeadCached(const Settings & settings, const std::string & actualUrl, bool shallow) { // Create a cache path to store the branch of the HEAD ref. Append something // in front of the URL to prevent collision with the repository itself. @@ -117,7 +117,7 @@ std::optional readHeadCached(const std::string & actualUrl, bool sh std::optional cachedRef; if (st) { cachedRef = readHead(cacheDir); - if (cachedRef != std::nullopt && *cachedRef != gitInitialBranch && isCacheFileWithinTtl(now, *st)) { + if (cachedRef != std::nullopt && *cachedRef != gitInitialBranch && isCacheFileWithinTtl(settings, now, *st)) { debug("using cached HEAD ref '%s' for repo '%s'", *cachedRef, actualUrl); return cachedRef; } @@ -728,12 +728,12 @@ struct GitInputScheme : InputScheme return revCount; } - std::string getDefaultRef(const RepoInfo & repoInfo, bool shallow) const + std::string getDefaultRef(const Settings & settings, const RepoInfo & repoInfo, bool shallow) const { auto head = std::visit( overloaded{ [&](const std::filesystem::path & path) { return GitRepo::openRepo(path, {})->getWorkdirRef(); }, - [&](const ParsedURL & url) { return readHeadCached(url.to_string(), shallow); }}, + [&](const ParsedURL & url) { return readHeadCached(settings, url.to_string(), shallow); }}, repoInfo.location); if (!head) { warn("could not read HEAD ref from repo at '%s', using 'master'", repoInfo.locationToArg()); @@ -783,7 +783,7 @@ struct GitInputScheme : InputScheme auto originalRef = input.getRef(); bool shallow = getShallowAttr(input); - auto ref = originalRef ? *originalRef : getDefaultRef(repoInfo, shallow); + auto ref = originalRef ? *originalRef : getDefaultRef(settings, repoInfo, shallow); input.attrs.insert_or_assign("ref", ref); std::filesystem::path repoDir; @@ -822,7 +822,7 @@ struct GitInputScheme : InputScheme /* If the local ref is older than 'tarball-ttl' seconds, do a git fetch to update the local ref to the remote ref. */ auto st = maybeStat(localRefFile); - doFetch = !st || !isCacheFileWithinTtl(now, *st); + doFetch = !st || !isCacheFileWithinTtl(settings, now, *st); } } diff --git a/src/libfetchers/include/nix/fetchers/fetch-settings.hh b/src/libfetchers/include/nix/fetchers/fetch-settings.hh index 8cfa7f609..2ab215a68 100644 --- a/src/libfetchers/include/nix/fetchers/fetch-settings.hh +++ b/src/libfetchers/include/nix/fetchers/fetch-settings.hh @@ -129,6 +129,25 @@ struct Settings : public Config true, Xp::Flakes}; + Setting tarballTtl{ + this, + 60 * 60, + "tarball-ttl", + R"( + The number of seconds a downloaded tarball is considered fresh. If + the cached tarball is stale, Nix checks whether it is still up + to date using the ETag header. Nix downloads a new version if + the ETag header is unsupported, or the cached ETag doesn't match. + + Setting the TTL to `0` forces Nix to always check if the tarball is + up to date. + + Nix caches tarballs in `$XDG_CACHE_HOME/nix/tarballs`. + + Files fetched via `NIX_PATH`, `fetchGit`, `fetchMercurial`, + `fetchTarball`, and `fetchurl` respect this TTL. + )"}; + ref getCache() const; ref getTarballCache() const; diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index d9b08a37f..fd56e3f1e 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -233,13 +233,13 @@ LegacyArgs::LegacyArgs( .longName = "keep-going", .shortName = 'k', .description = "Keep going after a build fails.", - .handler = {&(bool &) settings.keepGoing, true}, + .handler = {&(bool &) settings.getWorkerSettings().keepGoing, true}, }); addFlag({ .longName = "fallback", .description = "Build from source if substitution fails.", - .handler = {&(bool &) settings.tryFallback, true}, + .handler = {&(bool &) settings.getWorkerSettings().tryFallback, true}, }); auto intSettingAlias = diff --git a/src/libstore-test-support/test-main.cc b/src/libstore-test-support/test-main.cc index 1c17dd3e2..7c4a56ecc 100644 --- a/src/libstore-test-support/test-main.cc +++ b/src/libstore-test-support/test-main.cc @@ -15,10 +15,10 @@ int testMainForBuidingPre(int argc, char ** argv) } // Disable build hook. We won't be testing remote builds in these unit tests. If we do, fix the above build hook. - settings.buildHook = {}; + settings.getWorkerSettings().buildHook = {}; // No substituters, unless a test specifically requests. - settings.substituters = {}; + settings.getWorkerSettings().substituters = {}; #ifdef __linux__ // should match the conditional around sandboxBuildDir declaration. diff --git a/src/libstore-tests/derivation-advanced-attrs.cc b/src/libstore-tests/derivation-advanced-attrs.cc index 296ffed61..588ee9521 100644 --- a/src/libstore-tests/derivation-advanced-attrs.cc +++ b/src/libstore-tests/derivation-advanced-attrs.cc @@ -5,6 +5,7 @@ #include "nix/store/derivations.hh" #include "nix/store/derived-path.hh" #include "nix/store/derivation-options.hh" +#include "nix/store/globals.hh" #include "nix/store/parsed-derivations.hh" #include "nix/util/types.hh" #include "nix/util/json-utils.hh" @@ -194,7 +195,7 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults) EXPECT_EQ(options.canBuildLocally(*this->store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false); - EXPECT_EQ(options.substitutesAllowed(), true); + EXPECT_EQ(options.substitutesAllowed(settings.getWorkerSettings()), true); EXPECT_EQ(options.useUidRange(got), false); }); }; @@ -242,7 +243,7 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes) EXPECT_EQ(options, expected); - EXPECT_EQ(options.substitutesAllowed(), false); + EXPECT_EQ(options.substitutesAllowed(settings.getWorkerSettings()), false); EXPECT_EQ(options.useUidRange(got), true); }); }; @@ -336,7 +337,7 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_d EXPECT_EQ(options.canBuildLocally(*this->store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false); - EXPECT_EQ(options.substitutesAllowed(), true); + EXPECT_EQ(options.substitutesAllowed(settings.getWorkerSettings()), true); EXPECT_EQ(options.useUidRange(got), false); }); }; @@ -403,7 +404,7 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs) EXPECT_EQ(options.canBuildLocally(*this->store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false); - EXPECT_EQ(options.substitutesAllowed(), false); + EXPECT_EQ(options.substitutesAllowed(settings.getWorkerSettings()), false); EXPECT_EQ(options.useUidRange(got), true); }); }; diff --git a/src/libstore-tests/nix_api_store.cc b/src/libstore-tests/nix_api_store.cc index e67e5ffef..9bfbf8453 100644 --- a/src/libstore-tests/nix_api_store.cc +++ b/src/libstore-tests/nix_api_store.cc @@ -299,7 +299,7 @@ public: nix_api_store_test_base::SetUp(); nix::experimentalFeatureSettings.set("extra-experimental-features", "ca-derivations"); - nix::settings.substituters = {}; + nix::settings.getWorkerSettings().substituters = {}; store = open_local_store(); @@ -354,7 +354,7 @@ TEST_F(nix_api_store_test_base, build_from_json) { // FIXME get rid of these nix::experimentalFeatureSettings.set("extra-experimental-features", "ca-derivations"); - nix::settings.substituters = {}; + nix::settings.getWorkerSettings().substituters = {}; auto * store = open_local_store(); @@ -401,7 +401,7 @@ TEST_F(nix_api_store_test_base, nix_store_realise_invalid_system) { // Test that nix_store_realise properly reports errors when the system is invalid nix::experimentalFeatureSettings.set("extra-experimental-features", "ca-derivations"); - nix::settings.substituters = {}; + nix::settings.getWorkerSettings().substituters = {}; auto * store = open_local_store(); @@ -446,7 +446,7 @@ TEST_F(nix_api_store_test_base, nix_store_realise_builder_fails) { // Test that nix_store_realise properly reports errors when the builder fails nix::experimentalFeatureSettings.set("extra-experimental-features", "ca-derivations"); - nix::settings.substituters = {}; + nix::settings.getWorkerSettings().substituters = {}; auto * store = open_local_store(); @@ -491,7 +491,7 @@ TEST_F(nix_api_store_test_base, nix_store_realise_builder_no_output) { // Test that nix_store_realise properly reports errors when builder succeeds but produces no output nix::experimentalFeatureSettings.set("extra-experimental-features", "ca-derivations"); - nix::settings.substituters = {}; + nix::settings.getWorkerSettings().substituters = {}; auto * store = open_local_store(); @@ -687,7 +687,7 @@ TEST_F(NixApiStoreTestWithRealisedPath, nix_store_realise_output_ordering) // This test uses a CA derivation with 10 outputs in randomized input order // to verify that the callback order is deterministic and alphabetical. nix::experimentalFeatureSettings.set("extra-experimental-features", "ca-derivations"); - nix::settings.substituters = {}; + nix::settings.getWorkerSettings().substituters = {}; auto * store = open_local_store(); diff --git a/src/libstore/build/derivation-building-goal.cc b/src/libstore/build/derivation-building-goal.cc index 3cff4b2aa..1ce01fb81 100644 --- a/src/libstore/build/derivation-building-goal.cc +++ b/src/libstore/build/derivation-building-goal.cc @@ -63,7 +63,11 @@ std::string showKnownOutputs(const StoreDirConfig & store, const Derivation & dr } static void runPostBuildHook( - const StoreDirConfig & store, Logger & logger, const StorePath & drvPath, const StorePathSet & outputPaths); + const WorkerSettings & workerSettings, + const StoreDirConfig & store, + Logger & logger, + const StorePath & drvPath, + const StorePathSet & outputPaths); /* At least one of the output paths could not be produced using a substitute. So we have to build instead. */ @@ -87,7 +91,7 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution(bool storeDerivation) for (auto & i : drv->inputSrcs) { if (worker.store.isValidPath(i)) continue; - if (!settings.useSubstitutes) + if (!worker.settings.useSubstitutes) throw Error( "dependency '%s' of '%s' does not exist, and substitution is disabled", worker.store.printStorePath(i), @@ -325,7 +329,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild(StorePathSet inputPaths) `preferLocalBuild' set. Also, check and repair modes are only supported for local builds. */ bool buildLocally = (buildMode != bmNormal || drvOptions.willBuildLocally(worker.store, *drv)) - && settings.maxBuildJobs.get() != 0; + && worker.settings.maxBuildJobs.get() != 0; if (buildLocally) { useHook = false; @@ -476,7 +480,7 @@ Goal::Co DerivationBuildingGoal::buildWithHook( msg += fmt(" on '%s'", hook->machineName); std::unique_ptr buildLog = std::make_unique( - settings.logLines, + worker.settings.logLines, std::make_unique( *logger, lvlInfo, @@ -496,7 +500,7 @@ Goal::Co DerivationBuildingGoal::buildWithHook( auto & data = output->data; if (fd == hook->builderOut.readSide.get()) { logSize += data.size(); - if (settings.maxLogSize && logSize > settings.maxLogSize) { + if (worker.settings.maxLogSize && logSize > worker.settings.maxLogSize) { hook.reset(); co_return doneFailureLogTooLong(*buildLog); } @@ -605,7 +609,7 @@ Goal::Co DerivationBuildingGoal::buildWithHook( StorePathSet outputPaths; for (auto & [_, output] : builtOutputs) outputPaths.insert(output.outPath); - runPostBuildHook(worker.store, *logger, drvPath, outputPaths); + runPostBuildHook(worker.settings, worker.store, *logger, drvPath, outputPaths); /* It is now safe to delete the lock files, since all future lockers will see that the output paths are valid; they will @@ -647,7 +651,7 @@ Goal::Co DerivationBuildingGoal::buildLocally( : "building '%s'", worker.store.printStorePath(drvPath)); buildLog = std::make_unique( - settings.logLines, + worker.settings.logLines, std::make_unique( *logger, lvlInfo, actBuild, msg, Logger::Fields{worker.store.printStorePath(drvPath), "", 1, 1})); mcRunningBuilds = std::make_unique>(worker.runningBuilds); @@ -662,7 +666,7 @@ Goal::Co DerivationBuildingGoal::buildLocally( while (true) { unsigned int curBuilds = worker.getNrLocalBuilds(); - if (curBuilds >= settings.maxBuildJobs) { + if (curBuilds >= worker.settings.maxBuildJobs) { outputLocks.unlock(); co_await waitForBuildSlot(); co_return tryToBuild(std::move(inputPaths)); @@ -791,7 +795,7 @@ Goal::Co DerivationBuildingGoal::buildLocally( if (auto * output = std::get_if(&event)) { if (output->fd == builder->builderOut.get()) { logSize += output->data.size(); - if (settings.maxLogSize && logSize > settings.maxLogSize) { + if (worker.settings.maxLogSize && logSize > worker.settings.maxLogSize) { builder->killChild(); co_return doneFailureLogTooLong(*buildLog); } @@ -838,7 +842,7 @@ Goal::Co DerivationBuildingGoal::buildLocally( worker.markContentsGood(output.outPath); outputPaths.insert(output.outPath); } - runPostBuildHook(worker.store, *logger, drvPath, outputPaths); + runPostBuildHook(worker.settings, worker.store, *logger, drvPath, outputPaths); /* It is now safe to delete the lock files, since all future lockers will see that the output paths are valid; they will @@ -852,9 +856,13 @@ Goal::Co DerivationBuildingGoal::buildLocally( } static void runPostBuildHook( - const StoreDirConfig & store, Logger & logger, const StorePath & drvPath, const StorePathSet & outputPaths) + const WorkerSettings & workerSettings, + const StoreDirConfig & store, + Logger & logger, + const StorePath & drvPath, + const StorePathSet & outputPaths) { - auto hook = settings.postBuildHook; + auto hook = workerSettings.postBuildHook; if (hook == "") return; @@ -862,7 +870,7 @@ static void runPostBuildHook( logger, lvlTalkative, actPostBuildHook, - fmt("running post-build-hook '%s'", settings.postBuildHook), + fmt("running post-build-hook '%s'", workerSettings.postBuildHook), Logger::Fields{store.printStorePath(drvPath)}); PushActivity pact(act.id); StringMap hookEnvironment = getEnv(); @@ -910,7 +918,7 @@ static void runPostBuildHook( LogSink sink(act); runProgram2({ - .program = settings.postBuildHook, + .program = workerSettings.postBuildHook, .environment = hookEnvironment, .standardOut = &sink, .mergeStderrToStdout = true, @@ -957,17 +965,18 @@ HookReply DerivationBuildingGoal::tryBuildHook(const DerivationOptions(); + worker.hook = std::make_unique(worker.settings.buildHook); try { /* Send the request to the hook. */ - worker.hook->sink << "try" << (worker.getNrLocalBuilds() < settings.maxBuildJobs ? 1 : 0) << drv->platform - << worker.store.printStorePath(drvPath) << drvOptions.getRequiredSystemFeatures(*drv); + worker.hook->sink << "try" << (worker.getNrLocalBuilds() < worker.settings.maxBuildJobs ? 1 : 0) + << drv->platform << worker.store.printStorePath(drvPath) + << drvOptions.getRequiredSystemFeatures(*drv); worker.hook->sink.flush(); /* Read the first line of input, which should be a word indicating @@ -1073,7 +1082,7 @@ Goal::Done DerivationBuildingGoal::doneFailureLogTooLong(BuildLog & buildLog) BuildResult::Failure::LogLimitExceeded, "%s killed after writing more than %d bytes of log output", getName(), - settings.maxLogSize)); + worker.settings.maxLogSize)); } std::map> DerivationBuildingGoal::queryPartialDerivationOutputMap() diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index c4ce3c2b9..4f99928d7 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -100,7 +100,7 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation) /* 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 && drvOptions.substitutesAllowed()) { + if (worker.settings.useSubstitutes && drvOptions.substitutesAllowed(worker.settings)) { if (!checkResult) { DrvOutput id{outputHash, wantedOutput}; auto g = worker.makeDrvOutputSubstitutionGoal(id); @@ -133,7 +133,7 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation) assert(!drv->type().isImpure()); - if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback) { + if (nrFailed > 0 && nrFailed > nrNoSubstituters && !worker.settings.tryFallback) { co_return doneFailure(BuildError( BuildResult::Failure::TransientFailure, "some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ", diff --git a/src/libstore/build/goal.cc b/src/libstore/build/goal.cc index 618a7294c..d64b869e9 100644 --- a/src/libstore/build/goal.cc +++ b/src/libstore/build/goal.cc @@ -232,7 +232,7 @@ Goal::Done Goal::amDone(ExitCode result) if (goal->waitees.empty()) { worker.wakeUp(goal); - } else if (result == ecFailed && !settings.keepGoing) { + } else if (result == ecFailed && !worker.settings.keepGoing) { /* If we failed and keepGoing is not set, we remove all remaining waitees. */ for (auto & g : goal->waitees) { diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc index 3ee6b8987..541487bbc 100644 --- a/src/libstore/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -145,7 +145,7 @@ Goal::Co PathSubstitutionGoal::init() worker.updateProgress(); } if (lastStoresException.has_value()) { - if (!settings.tryFallback) { + if (!worker.settings.tryFallback) { throw *lastStoresException; } else logError(lastStoresException->info()); @@ -197,7 +197,7 @@ Goal::Co PathSubstitutionGoal::tryToRun( /* Make sure that we are allowed to start a substitution. Note that even if maxSubstitutionJobs == 0, we still allow a substituter to run. This prevents infinite waiting. */ - while (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) { + while (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) worker.settings.maxSubstitutionJobs)) { co_await waitForBuildSlot(); } diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 5aff13055..3fb843738 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -22,7 +22,10 @@ Worker::Worker(Store & store, Store & evalStore) , actSubstitutions(*logger, actCopyPaths) , store(store) , evalStore(evalStore) - , getSubstituters{[] { return settings.useSubstitutes ? getDefaultSubstituters() : std::list>{}; }} + , settings(nix::settings.getWorkerSettings()) + , getSubstituters{[] { + return nix::settings.getWorkerSettings().useSubstitutes ? getDefaultSubstituters() : std::list>{}; + }} { nrLocalBuilds = 0; nrSubstitutions = 0; diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index e26df204d..25eb338a1 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -233,14 +233,14 @@ struct ClientSettings void apply(TrustedFlag trusted) { settings.keepFailed = keepFailed; - settings.keepGoing = keepGoing; - settings.tryFallback = tryFallback; + settings.getWorkerSettings().keepGoing = keepGoing; + settings.getWorkerSettings().tryFallback = tryFallback; nix::verbosity = verbosity; - settings.maxBuildJobs.assign(maxBuildJobs); - settings.maxSilentTime = maxSilentTime; + settings.getWorkerSettings().maxBuildJobs.assign(maxBuildJobs); + settings.getWorkerSettings().maxSilentTime = maxSilentTime; settings.verboseBuild = verboseBuild; - settings.buildCores = buildCores; - settings.useSubstitutes = useSubstitutes; + settings.getLocalSettings().buildCores = buildCores; + settings.getWorkerSettings().useSubstitutes = useSubstitutes; for (auto & i : overrides) { auto & name(i.first); @@ -250,7 +250,7 @@ struct ClientSettings if (name != res.name && res.aliases.count(name) == 0) return false; std::set trusted = settings.trustedSubstituters; - for (auto & ref : settings.substituters.get()) + for (auto & ref : settings.getWorkerSettings().substituters.get()) trusted.insert(ref); std::vector subs; auto ss = tokenizeString(value); @@ -293,11 +293,12 @@ struct ClientSettings "Ignoring the client-specified plugin-files.\n" "The client specifying plugins to the daemon never made sense, and was removed in Nix >=2.14."); } else if ( - trusted || name == settings.buildTimeout.name || name == settings.maxSilentTime.name - || name == settings.pollInterval.name || name == "connect-timeout" + trusted || name == settings.getWorkerSettings().buildTimeout.name + || name == settings.getWorkerSettings().maxSilentTime.name + || name == settings.getWorkerSettings().pollInterval.name || name == "connect-timeout" || (name == "builders" && value == "")) settings.set(name, value); - else if (setSubstituters(settings.substituters)) + else if (setSubstituters(settings.getWorkerSettings().substituters)) ; else warn( diff --git a/src/libstore/derivation-options.cc b/src/libstore/derivation-options.cc index 7bb2ffb24..e352e5a47 100644 --- a/src/libstore/derivation-options.cc +++ b/src/libstore/derivation-options.cc @@ -366,7 +366,7 @@ bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDe && !drv.isBuiltin()) return false; - if (settings.maxBuildJobs.get() == 0 && !drv.isBuiltin()) + if (settings.getWorkerSettings().maxBuildJobs.get() == 0 && !drv.isBuiltin()) return false; for (auto & feature : getRequiredSystemFeatures(drv)) @@ -383,9 +383,9 @@ bool DerivationOptions::willBuildLocally(Store & localStore, const BasicD } template -bool DerivationOptions::substitutesAllowed() const +bool DerivationOptions::substitutesAllowed(const WorkerSettings & workerSettings) const { - return settings.alwaysAllowSubstitutes ? true : allowSubstitutes; + return workerSettings.alwaysAllowSubstitutes ? true : allowSubstitutes; } template diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index e0c12fb44..42fca014d 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -94,7 +94,7 @@ std::optional HttpBinaryCacheStore::getCompressionMethod(const void HttpBinaryCacheStore::maybeDisable() { auto state(_state.lock()); - if (state->enabled && settings.tryFallback) { + if (state->enabled && settings.getWorkerSettings().tryFallback) { int t = 60; printError("disabling binary cache '%s' for %s seconds", config->getHumanReadableURI(), t); state->enabled = false; diff --git a/src/libstore/include/nix/store/build/worker.hh b/src/libstore/include/nix/store/build/worker.hh index a26f73830..c65387de4 100644 --- a/src/libstore/include/nix/store/build/worker.hh +++ b/src/libstore/include/nix/store/build/worker.hh @@ -16,6 +16,7 @@ namespace nix { /* Forward definition. */ +struct WorkerSettings; struct DerivationTrampolineGoal; struct DerivationGoal; struct DerivationResolutionGoal; @@ -156,6 +157,7 @@ public: Store & store; Store & evalStore; + const WorkerSettings & settings; /** * Function to get the substituters to use for path substitution. diff --git a/src/libstore/include/nix/store/derivation-options.hh b/src/libstore/include/nix/store/derivation-options.hh index d733df159..4d17f190f 100644 --- a/src/libstore/include/nix/store/derivation-options.hh +++ b/src/libstore/include/nix/store/derivation-options.hh @@ -10,6 +10,7 @@ #include "nix/util/json-impls.hh" #include "nix/store/store-dir-config.hh" #include "nix/store/downstream-placeholder.hh" +#include "nix/store/worker-settings.hh" namespace nix { @@ -193,7 +194,7 @@ struct DerivationOptions */ bool willBuildLocally(Store & localStore, const BasicDerivation & drv) const; - bool substitutesAllowed() const; + bool substitutesAllowed(const WorkerSettings & workerSettings) const; /** * @param drv See note on `getRequiredSystemFeatures` diff --git a/src/libstore/include/nix/store/global-paths.hh b/src/libstore/include/nix/store/global-paths.hh new file mode 100644 index 000000000..4eb4cdef2 --- /dev/null +++ b/src/libstore/include/nix/store/global-paths.hh @@ -0,0 +1,33 @@ +#pragma once +///@file + +#include +#include + +namespace nix { + +/** + * The directory where system configuration files are stored. + * + * This is needed very early during initialization, before a main + * `Settings` object can be constructed. + */ +const std::filesystem::path & nixConfDir(); + +/** + * The path to the system configuration file (`nix.conf`). + */ +static inline std::filesystem::path nixConfFile() +{ + return nixConfDir() / "nix.conf"; +} + +/** + * A list of user configuration files to load. + * + * This is needed very early during initialization, before a main + * `Settings` object can be constructed. + */ +const std::vector & nixUserConfFiles(); + +} // namespace nix diff --git a/src/libstore/include/nix/store/globals.hh b/src/libstore/include/nix/store/globals.hh index 7659cc69b..44c45526f 100644 --- a/src/libstore/include/nix/store/globals.hh +++ b/src/libstore/include/nix/store/globals.hh @@ -9,51 +9,12 @@ #include "nix/store/build/derivation-builder.hh" #include "nix/store/local-settings.hh" #include "nix/store/store-reference.hh" +#include "nix/store/worker-settings.hh" #include "nix/store/config.hh" namespace nix { -struct MaxBuildJobsSetting : public BaseSetting -{ - MaxBuildJobsSetting( - Config * options, - unsigned int def, - const std::string & name, - const std::string & description, - const StringSet & aliases = {}) - : BaseSetting(def, true, name, description, aliases) - { - options->addSetting(this); - } - - unsigned int parse(const std::string & str) const override; -}; - -/** - * The directory where system configuration files are stored. - * - * This is needed very early during initialization, before a main - * `Settings` object can be constructed. - */ -const std::filesystem::path & nixConfDir(); - -/** - * The path to the system configuration file (`nix.conf`). - */ -static inline std::filesystem::path nixConfFile() -{ - return nixConfDir() / "nix.conf"; -} - -/** - * A list of user configuration files to load. - * - * This is needed very early during initialization, before a main - * `Settings` object can be constructed. - */ -const std::vector & nixUserConfFiles(); - struct LogFileSettings : public virtual Config { /** @@ -89,7 +50,7 @@ public: {"build-compress-log"}}; }; -class Settings : public virtual Config, private LocalSettings, private LogFileSettings +class Settings : public virtual Config, private LocalSettings, private LogFileSettings, private WorkerSettings { StringSet getDefaultSystemFeatures(); @@ -129,6 +90,19 @@ public: return *this; } + /** + * Get the worker settings. + */ + WorkerSettings & getWorkerSettings() + { + return *this; + } + + const WorkerSettings & getWorkerSettings() const + { + return *this; + } + static unsigned int getDefaultCores(); /** @@ -162,86 +136,11 @@ public: Setting keepFailed{this, false, "keep-failed", "Whether to keep temporary directories of failed builds."}; - Setting keepGoing{ - this, false, "keep-going", "Whether to keep building derivations when another build fails."}; - - Setting tryFallback{ - this, - false, - "fallback", - R"( - If set to `true`, Nix falls back to building from source if a - binary substitute fails. This is equivalent to the `--fallback` - flag. The default is `false`. - )", - {"build-fallback"}}; - /** * Whether to show build log output in real time. */ bool verboseBuild = true; - Setting logLines{ - this, - 25, - "log-lines", - "The number of lines of the tail of " - "the log to show if a build fails."}; - - MaxBuildJobsSetting maxBuildJobs{ - this, - 1, - "max-jobs", - R"( - Maximum number of jobs that Nix tries to build locally in parallel. - - The special value `auto` causes Nix to use the number of CPUs in your system. - Use `0` to disable local builds and directly use the remote machines specified in [`builders`](#conf-builders). - This doesn't affect derivations that have [`preferLocalBuild = true`](@docroot@/language/advanced-attributes.md#adv-attr-preferLocalBuild), which are always built locally. - - > **Note** - > - > The number of CPU cores to use for each build job is independently determined by the [`cores`](#conf-cores) setting. - - - The setting can be overridden using the `--max-jobs` (`-j`) command line switch. - )", - {"build-max-jobs"}}; - - Setting maxSubstitutionJobs{ - this, - 16, - "max-substitution-jobs", - R"( - This option defines the maximum number of substitution jobs that Nix - tries to run in parallel. The default is `16`. The minimum value - one can choose is `1` and lower values are interpreted as `1`. - )", - {"substitution-max-jobs"}}; - - Setting buildCores{ - this, - 0, - "cores", - R"( - Sets the value of the `NIX_BUILD_CORES` environment variable in the [invocation of the `builder` executable](@docroot@/store/building.md#builder-execution) of a derivation. - The `builder` executable can use this variable to control its own maximum amount of parallelism. - - - For instance, in Nixpkgs, if the attribute `enableParallelBuilding` for the `mkDerivation` build helper is set to `true`, it passes the `-j${NIX_BUILD_CORES}` flag to GNU Make. - - If set to `0`, nix will detect the number of CPU cores and pass this number via `NIX_BUILD_CORES`. - - > **Note** - > - > The number of parallel local Nix build jobs is independently controlled with the [`max-jobs`](#conf-max-jobs) setting. - )", - {"build-cores"}}; - /** * Read-only mode. Don't copy stuff to the store, don't change * the database. @@ -277,219 +176,6 @@ public: configuration option is set as the empty string. )"}; - Setting maxSilentTime{ - this, - 0, - "max-silent-time", - R"( - This option defines the maximum number of seconds that a builder can - go without producing any data on standard output or standard error. - This is useful (for instance in an automated build system) to catch - builds that are stuck in an infinite loop, or to catch remote builds - that are hanging due to network problems. It can be overridden using - the `--max-silent-time` command line switch. - - The value `0` means that there is no timeout. This is also the - default. - )", - {"build-max-silent-time"}}; - - Setting buildTimeout{ - this, - 0, - "timeout", - R"( - This option defines the maximum number of seconds that a builder can - run. This is useful (for instance in an automated build system) to - catch builds that are stuck in an infinite loop but keep writing to - their standard output or standard error. It can be overridden using - the `--timeout` command line switch. - - The value `0` means that there is no timeout. This is also the - default. - )", - {"build-timeout"}}; - - Setting buildHook{ - this, - {"nix", "__build-remote"}, - "build-hook", - R"( - The path to the helper program that executes remote builds. - - Nix communicates with the build hook over `stdio` using a custom protocol to request builds that cannot be performed directly by the Nix daemon. - The default value is the internal Nix binary that implements remote building. - - > **Important** - > - > Change this setting only if you really know what you’re doing. - )"}; - - Setting builders{ - this, - "@" + (nixConfDir() / "machines").string(), - "builders", - R"( - A semicolon- or newline-separated list of build machines. - - In addition to the [usual ways of setting configuration options](@docroot@/command-ref/conf-file.md), the value can be read from a file by prefixing its absolute path with `@`. - - > **Example** - > - > This is the default setting: - > - > ``` - > builders = @/etc/nix/machines - > ``` - - Each machine specification consists of the following elements, separated by spaces. - Only the first element is required. - To leave a field at its default, set it to `-`. - - 1. The URI of the remote store in the format `ssh://[username@]hostname[:port]`. - - > **Example** - > - > `ssh://nix@mac` - - For backward compatibility, `ssh://` may be omitted. - The hostname may be an alias defined in `~/.ssh/config`. - - 2. A comma-separated list of [Nix system types](@docroot@/development/building.md#system-type). - If omitted, this defaults to the local platform type. - - > **Example** - > - > `aarch64-darwin` - - It is possible for a machine to support multiple platform types. - - > **Example** - > - > `i686-linux,x86_64-linux` - - 3. The SSH identity file to be used to log in to the remote machine. - If omitted, SSH uses its regular identities. - - > **Example** - > - > `/home/user/.ssh/id_mac` - - 4. The maximum number of builds that Nix executes in parallel on the machine. - Typically this should be equal to the number of CPU cores. - - 5. The “speed factor”, indicating the relative speed of the machine as a positive integer. - If there are multiple machines of the right type, Nix prefers the fastest, taking load into account. - - 6. A comma-separated list of supported [system features](#conf-system-features). - - A machine is only used to build a derivation if all the features in the derivation's [`requiredSystemFeatures`](@docroot@/language/advanced-attributes.html#adv-attr-requiredSystemFeatures) attribute are supported by that machine. - - 7. A comma-separated list of required [system features](#conf-system-features). - - A machine is only used to build a derivation if all of the machine’s required features appear in the derivation’s [`requiredSystemFeatures`](@docroot@/language/advanced-attributes.html#adv-attr-requiredSystemFeatures) attribute. - - 8. The (base64-encoded) public host key of the remote machine. - If omitted, SSH uses its regular `known_hosts` file. - - The value for this field can be obtained via `base64 -w0`. - - > **Example** - > - > Multiple builders specified on the command line: - > - > ```console - > --builders 'ssh://mac x86_64-darwin ; ssh://beastie x86_64-freebsd' - > ``` - - > **Example** - > - > This specifies several machines that can perform `i686-linux` builds: - > - > ``` - > nix@scratchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy 8 1 kvm - > nix@itchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy 8 2 - > nix@poochie.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy 1 2 kvm benchmark - > ``` - > - > However, `poochie` only builds derivations that have the attribute - > - > ```nix - > requiredSystemFeatures = [ "benchmark" ]; - > ``` - > - > or - > - > ```nix - > requiredSystemFeatures = [ "benchmark" "kvm" ]; - > ``` - > - > `itchy` cannot do builds that require `kvm`, but `scratchy` does support such builds. - > For regular builds, `itchy` is preferred over `scratchy` because it has a higher speed factor. - - For Nix to use substituters, the calling user must be in the [`trusted-users`](#conf-trusted-users) list. - - > **Note** - > - > A build machine must be accessible via SSH and have Nix installed. - > `nix` must be available in `$PATH` for the user connecting over SSH. - - > **Warning** - > - > If you are building via the Nix daemon (default), the Nix daemon user account on the local machine (that is, `root`) requires access to a user account on the remote machine (not necessarily `root`). - > - > If you can’t or don’t want to configure `root` to be able to access the remote machine, set [`store`](#conf-store) to any [local store](@docroot@/store/types/local-store.html), e.g. by passing `--store /tmp` to the command on the local machine. - - To build only on remote machines and disable local builds, set [`max-jobs`](#conf-max-jobs) to 0. - - If you want the remote machines to use substituters, set [`builders-use-substitutes`](#conf-builders-use-substitutes) to `true`. - )", - {}, - false}; - - Setting alwaysAllowSubstitutes{ - this, - false, - "always-allow-substitutes", - R"( - If set to `true`, Nix ignores the [`allowSubstitutes`](@docroot@/language/advanced-attributes.md) attribute in derivations and always attempt to use [available substituters](#conf-substituters). - )"}; - - Setting buildersUseSubstitutes{ - this, - false, - "builders-use-substitutes", - R"( - If set to `true`, Nix instructs [remote build machines](#conf-builders) to use their own [`substituters`](#conf-substituters) if available. - - It means that remote build hosts fetch as many dependencies as possible from their own substituters (e.g, from `cache.nixos.org`) instead of waiting for the local machine to upload them all. - This can drastically reduce build times if the network connection between the local machine and the remote build host is slow. - )"}; - - Setting useSubstitutes{ - this, - true, - "substitute", - R"( - If set to `true` (default), Nix uses binary substitutes if - available. This option can be disabled to force building from - source. - )", - {"build-use-substitutes"}}; - - Setting maxLogSize{ - this, - 0, - "max-build-log-size", - R"( - This option defines the maximum number of bytes that a builder can - write to its stdout/stderr. If the builder exceeds this limit, it’s - killed. A value of `0` (the default) means that there is no limit. - )", - {"build-max-log-size"}}; - - Setting pollInterval{this, 5, "build-poll-interval", "How often (in seconds) to poll for locks."}; - Setting trustedPublicKeys{ this, {"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="}, @@ -520,25 +206,6 @@ public: can add it to `trusted-public-keys` in their `nix.conf`. )"}; - Setting tarballTtl{ - this, - 60 * 60, - "tarball-ttl", - R"( - The number of seconds a downloaded tarball is considered fresh. If - the cached tarball is stale, Nix checks whether it is still up - to date using the ETag header. Nix downloads a new version if - the ETag header is unsupported, or the cached ETag doesn't match. - - Setting the TTL to `0` forces Nix to always check if the tarball is - up to date. - - Nix caches tarballs in `$XDG_CACHE_HOME/nix/tarballs`. - - Files fetched via `NIX_PATH`, `fetchGit`, `fetchMercurial`, - `fetchTarball`, and `fetchurl` respect this TTL. - )"}; - Setting requireSigs{ this, true, @@ -629,27 +296,9 @@ public: // Don't document the machine-specific default value false}; - Setting> substituters{ - this, - std::vector{StoreReference::parse("https://cache.nixos.org/")}, - "substituters", - R"( - A list of [URLs of Nix stores](@docroot@/store/types/index.md#store-url-format) to be used as substituters, separated by whitespace. - A substituter is an additional [store](@docroot@/glossary.md#gloss-store) from which Nix can obtain [store objects](@docroot@/store/store-object.md) instead of building them. - - Substituters are tried based on their priority value, which each substituter can set independently. - Lower value means higher priority. - The default is `https://cache.nixos.org`, which has a priority of 40. - - At least one of the following conditions must be met for Nix to use a substituter: - - - The substituter is in the [`trusted-substituters`](#conf-trusted-substituters) list - - The user calling Nix is in the [`trusted-users`](#conf-trusted-users) list - - In addition, each store path should be trusted as described in [`trusted-public-keys`](#conf-trusted-public-keys) - )", - {"binary-caches"}}; - + // move to daemonsettings in another pass + // + // we'll add a another parameter to processConnection to thread it through Setting> trustedSubstituters{ this, {}, @@ -695,56 +344,10 @@ public: mismatch if the build isn't reproducible. )"}; + // move it out in the 2nd pass Setting printMissing{ this, true, "print-missing", "Whether to print what paths need to be built or downloaded."}; - Setting postBuildHook{ - this, - "", - "post-build-hook", - R"( - Optional. The path to a program to execute after each build. - - This option is only settable in the global `nix.conf`, or on the - command line by trusted users. - - When using the nix-daemon, the daemon executes the hook as `root`. - If the nix-daemon is not involved, the hook runs as the user - executing the nix-build. - - - The hook executes after an evaluation-time build. - - - The hook does not execute on substituted paths. - - - The hook's output always goes to the user's terminal. - - - If the hook fails, the build succeeds but no further builds - execute. - - - The hook executes synchronously, and blocks other builds from - progressing while it runs. - - The program executes with no arguments. The program's environment - contains the following environment variables: - - - `DRV_PATH` - The derivation for the built paths. - - Example: - `/nix/store/5nihn1a7pa8b25l9zafqaqibznlvvp3f-bash-4.4-p23.drv` - - - `OUT_PATHS` - Output paths of the built derivation, separated by a space - character. - - Example: - `/nix/store/l88brggg9hpy96ijds34dlq4n8fan63g-bash-4.4-p23-dev - /nix/store/vch71bhyi5akr5zs40k8h2wqxx69j80l-bash-4.4-p23-doc - /nix/store/c5cxjywi66iwn9dcx5yvwjkvl559ay6p-bash-4.4-p23-info - /nix/store/scz72lskj03ihkcn42ias5mlp4i4gr1k-bash-4.4-p23-man - /nix/store/a724znygmd1cac856j3gfsyvih3lw07j-bash-4.4-p23`. - )"}; - Setting useXDGBaseDirectories{ this, false, diff --git a/src/libstore/include/nix/store/local-settings.hh b/src/libstore/include/nix/store/local-settings.hh index f9bddb2e6..0fa8499bf 100644 --- a/src/libstore/include/nix/store/local-settings.hh +++ b/src/libstore/include/nix/store/local-settings.hh @@ -185,6 +185,29 @@ struct LocalSettings : public virtual Config, public GCSettings, public AutoAllo return autoAllocateUids ? this : nullptr; } + Setting buildCores{ + this, + 0, + "cores", + R"( + Sets the value of the `NIX_BUILD_CORES` environment variable in the [invocation of the `builder` executable](@docroot@/store/building.md#builder-execution) of a derivation. + The `builder` executable can use this variable to control its own maximum amount of parallelism. + + + For instance, in Nixpkgs, if the attribute `enableParallelBuilding` for the `mkDerivation` build helper is set to `true`, it passes the `-j${NIX_BUILD_CORES}` flag to GNU Make. + + If set to `0`, nix will detect the number of CPU cores and pass this number via `NIX_BUILD_CORES`. + + > **Note** + > + > The number of parallel local Nix build jobs is independently controlled with the [`max-jobs`](#conf-max-jobs) setting. + )", + {"build-cores"}}; + Setting fsyncMetadata{ this, true, diff --git a/src/libstore/include/nix/store/meson.build b/src/libstore/include/nix/store/meson.build index 64fd9c0fb..93be5921a 100644 --- a/src/libstore/include/nix/store/meson.build +++ b/src/libstore/include/nix/store/meson.build @@ -42,6 +42,7 @@ headers = [ config_pub_h ] + files( 'export-import.hh', 'filetransfer.hh', 'gc-store.hh', + 'global-paths.hh', 'globals.hh', 'http-binary-cache-store.hh', 'indirect-root-store.hh', @@ -94,4 +95,5 @@ headers = [ config_pub_h ] + files( 'worker-protocol-connection.hh', 'worker-protocol-impl.hh', 'worker-protocol.hh', + 'worker-settings.hh', ) diff --git a/src/libstore/include/nix/store/worker-settings.hh b/src/libstore/include/nix/store/worker-settings.hh new file mode 100644 index 000000000..13fa0261d --- /dev/null +++ b/src/libstore/include/nix/store/worker-settings.hh @@ -0,0 +1,366 @@ +#pragma once +///@file + +#include "nix/util/configuration.hh" +#include "nix/store/global-paths.hh" +#include "nix/store/store-reference.hh" + +namespace nix { + +struct MaxBuildJobsSetting : public BaseSetting +{ + MaxBuildJobsSetting( + Config * options, + unsigned int def, + const std::string & name, + const std::string & description, + const StringSet & aliases = {}) + : BaseSetting(def, true, name, description, aliases) + { + options->addSetting(this); + } + + unsigned int parse(const std::string & str) const override; +}; + +struct WorkerSettings : public virtual Config +{ +protected: + WorkerSettings() = default; + +public: + Setting keepGoing{ + this, false, "keep-going", "Whether to keep building derivations when another build fails."}; + + Setting tryFallback{ + this, + false, + "fallback", + R"( + If set to `true`, Nix falls back to building from source if a + binary substitute fails. This is equivalent to the `--fallback` + flag. The default is `false`. + )", + {"build-fallback"}}; + + Setting logLines{ + this, + 25, + "log-lines", + "The number of lines of the tail of " + "the log to show if a build fails."}; + + MaxBuildJobsSetting maxBuildJobs{ + this, + 1, + "max-jobs", + R"( + Maximum number of jobs that Nix tries to build locally in parallel. + + The special value `auto` causes Nix to use the number of CPUs in your system. + Use `0` to disable local builds and directly use the remote machines specified in [`builders`](#conf-builders). + This doesn't affect derivations that have [`preferLocalBuild = true`](@docroot@/language/advanced-attributes.md#adv-attr-preferLocalBuild), which are always built locally. + + > **Note** + > + > The number of CPU cores to use for each build job is independently determined by the [`cores`](#conf-cores) setting. + + + The setting can be overridden using the `--max-jobs` (`-j`) command line switch. + )", + {"build-max-jobs"}}; + + Setting maxSubstitutionJobs{ + this, + 16, + "max-substitution-jobs", + R"( + This option defines the maximum number of substitution jobs that Nix + tries to run in parallel. The default is `16`. The minimum value + one can choose is `1` and lower values are interpreted as `1`. + )", + {"substitution-max-jobs"}}; + + Setting maxSilentTime{ + this, + 0, + "max-silent-time", + R"( + This option defines the maximum number of seconds that a builder can + go without producing any data on standard output or standard error. + This is useful (for instance in an automated build system) to catch + builds that are stuck in an infinite loop, or to catch remote builds + that are hanging due to network problems. It can be overridden using + the `--max-silent-time` command line switch. + + The value `0` means that there is no timeout. This is also the + default. + )", + {"build-max-silent-time"}}; + + Setting buildTimeout{ + this, + 0, + "timeout", + R"( + This option defines the maximum number of seconds that a builder can + run. This is useful (for instance in an automated build system) to + catch builds that are stuck in an infinite loop but keep writing to + their standard output or standard error. It can be overridden using + the `--timeout` command line switch. + + The value `0` means that there is no timeout. This is also the + default. + )", + {"build-timeout"}}; + + Setting buildHook{ + this, + {"nix", "__build-remote"}, + "build-hook", + R"( + The path to the helper program that executes remote builds. + + Nix communicates with the build hook over `stdio` using a custom protocol to request builds that cannot be performed directly by the Nix daemon. + The default value is the internal Nix binary that implements remote building. + + > **Important** + > + > Change this setting only if you really know what you’re doing. + )"}; + + Setting builders{ + this, + "@" + (nixConfDir() / "machines").string(), + "builders", + R"( + A semicolon- or newline-separated list of build machines. + + In addition to the [usual ways of setting configuration options](@docroot@/command-ref/conf-file.md), the value can be read from a file by prefixing its absolute path with `@`. + + > **Example** + > + > This is the default setting: + > + > ``` + > builders = @/etc/nix/machines + > ``` + + Each machine specification consists of the following elements, separated by spaces. + Only the first element is required. + To leave a field at its default, set it to `-`. + + 1. The URI of the remote store in the format `ssh://[username@]hostname[:port]`. + + > **Example** + > + > `ssh://nix@mac` + + For backward compatibility, `ssh://` may be omitted. + The hostname may be an alias defined in `~/.ssh/config`. + + 2. A comma-separated list of [Nix system types](@docroot@/development/building.md#system-type). + If omitted, this defaults to the local platform type. + + > **Example** + > + > `aarch64-darwin` + + It is possible for a machine to support multiple platform types. + + > **Example** + > + > `i686-linux,x86_64-linux` + + 3. The SSH identity file to be used to log in to the remote machine. + If omitted, SSH uses its regular identities. + + > **Example** + > + > `/home/user/.ssh/id_mac` + + 4. The maximum number of builds that Nix executes in parallel on the machine. + Typically this should be equal to the number of CPU cores. + + 5. The “speed factor”, indicating the relative speed of the machine as a positive integer. + If there are multiple machines of the right type, Nix prefers the fastest, taking load into account. + + 6. A comma-separated list of supported [system features](#conf-system-features). + + A machine is only used to build a derivation if all the features in the derivation's [`requiredSystemFeatures`](@docroot@/language/advanced-attributes.html#adv-attr-requiredSystemFeatures) attribute are supported by that machine. + + 7. A comma-separated list of required [system features](#conf-system-features). + + A machine is only used to build a derivation if all of the machine’s required features appear in the derivation’s [`requiredSystemFeatures`](@docroot@/language/advanced-attributes.html#adv-attr-requiredSystemFeatures) attribute. + + 8. The (base64-encoded) public host key of the remote machine. + If omitted, SSH uses its regular `known_hosts` file. + + The value for this field can be obtained via `base64 -w0`. + + > **Example** + > + > Multiple builders specified on the command line: + > + > ```console + > --builders 'ssh://mac x86_64-darwin ; ssh://beastie x86_64-freebsd' + > ``` + + > **Example** + > + > This specifies several machines that can perform `i686-linux` builds: + > + > ``` + > nix@scratchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy 8 1 kvm + > nix@itchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy 8 2 + > nix@poochie.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy 1 2 kvm benchmark + > ``` + > + > However, `poochie` only builds derivations that have the attribute + > + > ```nix + > requiredSystemFeatures = [ "benchmark" ]; + > ``` + > + > or + > + > ```nix + > requiredSystemFeatures = [ "benchmark" "kvm" ]; + > ``` + > + > `itchy` cannot do builds that require `kvm`, but `scratchy` does support such builds. + > For regular builds, `itchy` is preferred over `scratchy` because it has a higher speed factor. + + For Nix to use substituters, the calling user must be in the [`trusted-users`](#conf-trusted-users) list. + + > **Note** + > + > A build machine must be accessible via SSH and have Nix installed. + > `nix` must be available in `$PATH` for the user connecting over SSH. + + > **Warning** + > + > If you are building via the Nix daemon (default), the Nix daemon user account on the local machine (that is, `root`) requires access to a user account on the remote machine (not necessarily `root`). + > + > If you can’t or don’t want to configure `root` to be able to access the remote machine, set [`store`](#conf-store) to any [local store](@docroot@/store/types/local-store.html), e.g. by passing `--store /tmp` to the command on the local machine. + + To build only on remote machines and disable local builds, set [`max-jobs`](#conf-max-jobs) to 0. + + If you want the remote machines to use substituters, set [`builders-use-substitutes`](#conf-builders-use-substitutes) to `true`. + )", + {}, + false}; + + Setting alwaysAllowSubstitutes{ + this, + false, + "always-allow-substitutes", + R"( + If set to `true`, Nix ignores the [`allowSubstitutes`](@docroot@/language/advanced-attributes.md) attribute in derivations and always attempt to use [available substituters](#conf-substituters). + )"}; + + Setting buildersUseSubstitutes{ + this, + false, + "builders-use-substitutes", + R"( + If set to `true`, Nix instructs [remote build machines](#conf-builders) to use their own [`substituters`](#conf-substituters) if available. + + It means that remote build hosts fetch as many dependencies as possible from their own substituters (e.g, from `cache.nixos.org`) instead of waiting for the local machine to upload them all. + This can drastically reduce build times if the network connection between the local machine and the remote build host is slow. + )"}; + + Setting useSubstitutes{ + this, + true, + "substitute", + R"( + If set to `true` (default), Nix uses binary substitutes if + available. This option can be disabled to force building from + source. + )", + {"build-use-substitutes"}}; + + Setting> substituters{ + this, + std::vector{StoreReference::parse("https://cache.nixos.org/")}, + "substituters", + R"( + A list of [URLs of Nix stores](@docroot@/store/types/index.md#store-url-format) to be used as substituters, separated by whitespace. + A substituter is an additional [store](@docroot@/glossary.md#gloss-store) from which Nix can obtain [store objects](@docroot@/store/store-object.md) instead of building them. + + Substituters are tried based on their priority value, which each substituter can set independently. + Lower value means higher priority. + The default is `https://cache.nixos.org`, which has a priority of 40. + + At least one of the following conditions must be met for Nix to use a substituter: + + - The substituter is in the [`trusted-substituters`](#conf-trusted-substituters) list + - The user calling Nix is in the [`trusted-users`](#conf-trusted-users) list + + In addition, each store path should be trusted as described in [`trusted-public-keys`](#conf-trusted-public-keys) + )", + {"binary-caches"}}; + + Setting maxLogSize{ + this, + 0, + "max-build-log-size", + R"( + This option defines the maximum number of bytes that a builder can + write to its stdout/stderr. If the builder exceeds this limit, it's + killed. A value of `0` (the default) means that there is no limit. + )", + {"build-max-log-size"}}; + + Setting pollInterval{this, 5, "build-poll-interval", "How often (in seconds) to poll for locks."}; + + Setting postBuildHook{ + this, + "", + "post-build-hook", + R"( + Optional. The path to a program to execute after each build. + + This option is only settable in the global `nix.conf`, or on the + command line by trusted users. + + When using the nix-daemon, the daemon executes the hook as `root`. + If the nix-daemon is not involved, the hook runs as the user + executing the nix-build. + + - The hook executes after an evaluation-time build. + + - The hook does not execute on substituted paths. + + - The hook's output always goes to the user's terminal. + + - If the hook fails, the build succeeds but no further builds + execute. + + - The hook executes synchronously, and blocks other builds from + progressing while it runs. + + The program executes with no arguments. The program's environment + contains the following environment variables: + + - `DRV_PATH` + The derivation for the built paths. + + Example: + `/nix/store/5nihn1a7pa8b25l9zafqaqibznlvvp3f-bash-4.4-p23.drv` + + - `OUT_PATHS` + Output paths of the built derivation, separated by a space + character. + + Example: + `/nix/store/l88brggg9hpy96ijds34dlq4n8fan63g-bash-4.4-p23-dev + /nix/store/vch71bhyi5akr5zs40k8h2wqxx69j80l-bash-4.4-p23-doc + /nix/store/c5cxjywi66iwn9dcx5yvwjkvl559ay6p-bash-4.4-p23-info + /nix/store/scz72lskj03ihkcn42ias5mlp4i4gr1k-bash-4.4-p23-man + /nix/store/a724znygmd1cac856j3gfsyvih3lw07j-bash-4.4-p23`. + )"}; +}; + +} // namespace nix diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 830582753..7c58c7226 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -183,9 +183,9 @@ void LegacySSHStore::narFromPath(const StorePath & path, std::function & targets) throw; } - if (!knownOutputPaths && settings.useSubstitutes && drvOptions.substitutesAllowed()) { + if (!knownOutputPaths && settings.getWorkerSettings().useSubstitutes + && drvOptions.substitutesAllowed(settings.getWorkerSettings())) { experimentalFeatureSettings.require(Xp::CaDerivations); // If there are unknown output paths, attempt to find if the @@ -266,7 +267,8 @@ MissingPaths Store::queryMissing(const std::vector & targets) } } - if (knownOutputPaths && settings.useSubstitutes && drvOptions.substitutesAllowed()) { + if (knownOutputPaths && settings.getWorkerSettings().useSubstitutes + && drvOptions.substitutesAllowed(settings.getWorkerSettings())) { auto drvState = make_ref>(DrvState(invalid.size())); for (auto & output : invalid) pool.enqueue(std::bind(checkOutput, drvPath, drv, output, drvState)); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 8bc1a5d63..f317e906e 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -114,22 +114,23 @@ void RemoteStore::initConnection(Connection & conn) void RemoteStore::setOptions(Connection & conn) { - conn.to << WorkerProto::Op::SetOptions << settings.keepFailed << settings.keepGoing << settings.tryFallback - << verbosity << settings.maxBuildJobs << settings.maxSilentTime << true - << (settings.verboseBuild ? lvlError : lvlVomit) << 0 // obsolete log type - << 0 /* obsolete print build trace */ - << settings.buildCores << settings.useSubstitutes; + conn.to << WorkerProto::Op::SetOptions << settings.keepFailed << settings.getWorkerSettings().keepGoing + << settings.getWorkerSettings().tryFallback << verbosity << settings.getWorkerSettings().maxBuildJobs + << settings.getWorkerSettings().maxSilentTime << true << (settings.verboseBuild ? lvlError : lvlVomit) + << 0 // obsolete log type + << 0 /* obsolete print build trace */ + << settings.getLocalSettings().buildCores << settings.getWorkerSettings().useSubstitutes; std::map overrides; settings.getSettings(overrides, true); // libstore settings fileTransferSettings.getSettings(overrides, true); overrides.erase(settings.keepFailed.name); - overrides.erase(settings.keepGoing.name); - overrides.erase(settings.tryFallback.name); - overrides.erase(settings.maxBuildJobs.name); - overrides.erase(settings.maxSilentTime.name); - overrides.erase(settings.buildCores.name); - overrides.erase(settings.useSubstitutes.name); + overrides.erase(settings.getWorkerSettings().keepGoing.name); + overrides.erase(settings.getWorkerSettings().tryFallback.name); + overrides.erase(settings.getWorkerSettings().maxBuildJobs.name); + overrides.erase(settings.getWorkerSettings().maxSilentTime.name); + overrides.erase(settings.getLocalSettings().buildCores.name); + overrides.erase(settings.getWorkerSettings().useSubstitutes.name); overrides.erase(loggerSettings.showTrace.name); overrides.erase(experimentalFeatureSettings.experimentalFeatures.name); overrides.erase("plugin-files"); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 9bb304055..bc7df176b 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -176,7 +176,7 @@ void Store::addMultipleToStore(PathsSource && pathsToCopy, Activity & act, Repai addToStore(info, *source, repair, checkSigs); } catch (Error & e) { nrFailed++; - if (!settings.keepGoing) + if (!settings.getWorkerSettings().keepGoing) throw e; printMsg(lvlError, "could not copy %s: %s", printStorePath(path), e.what()); showProgress(); @@ -407,7 +407,7 @@ StorePathSet Store::queryDerivationOutputs(const StorePath & path) void Store::querySubstitutablePathInfos(const StorePathCAMap & paths, SubstitutablePathInfos & infos) { - if (!settings.useSubstitutes) + if (!settings.getWorkerSettings().useSubstitutes) return; for (auto & path : paths) { @@ -463,7 +463,7 @@ void Store::querySubstitutablePathInfos(const StorePathCAMap & paths, Substituta } } if (lastStoresException.has_value()) { - if (!settings.tryFallback) { + if (!settings.getWorkerSettings().tryFallback) { throw *lastStoresException; } else logError(lastStoresException->info()); @@ -473,7 +473,7 @@ void Store::querySubstitutablePathInfos(const StorePathCAMap & paths, Substituta StorePathSet Store::querySubstitutablePaths(const StorePathSet & paths) { - if (!settings.useSubstitutes) + if (!settings.getWorkerSettings().useSubstitutes) return StorePathSet(); StorePathSet remaining; diff --git a/src/libstore/store-registration.cc b/src/libstore/store-registration.cc index 13bcec9cb..633556e48 100644 --- a/src/libstore/store-registration.cc +++ b/src/libstore/store-registration.cc @@ -91,7 +91,7 @@ std::list> getDefaultSubstituters() } }; - for (const auto & ref : settings.substituters.get()) + for (const auto & ref : settings.getWorkerSettings().substituters.get()) addStore(ref); stores.sort([](ref & a, ref & b) { return a->config.priority < b->config.priority; }); diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc index fbbedc358..1d25cb479 100644 --- a/src/libstore/unix/build/derivation-builder.cc +++ b/src/libstore/unix/build/derivation-builder.cc @@ -1076,7 +1076,9 @@ void DerivationBuilderImpl::initEnv() env["NIX_STORE"] = store.storeDir; /* The maximum number of cores to utilize for parallel building. */ - env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores ? settings.buildCores : settings.getDefaultCores()); + env["NIX_BUILD_CORES"] = fmt( + "%d", + settings.getLocalSettings().buildCores ? settings.getLocalSettings().buildCores : settings.getDefaultCores()); /* Write the final environment. Note that this is intentionally *not* `drv.env`, because we've desugared things like like diff --git a/src/libstore/unix/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc index af9249487..39be05440 100644 --- a/src/libstore/unix/build/hook-instance.cc +++ b/src/libstore/unix/build/hook-instance.cc @@ -1,18 +1,16 @@ -#include "nix/store/globals.hh" #include "nix/util/config-global.hh" #include "nix/store/build/hook-instance.hh" -#include "nix/util/file-system.hh" #include "nix/store/build/child.hh" #include "nix/util/strings.hh" #include "nix/util/executable-path.hh" namespace nix { -HookInstance::HookInstance() +HookInstance::HookInstance(const Strings & _buildHook) { - debug("starting build hook '%s'", concatStringsSep(" ", settings.buildHook.get())); + debug("starting build hook '%s'", concatStringsSep(" ", _buildHook)); - auto buildHookArgs = settings.buildHook.get(); + auto buildHookArgs = _buildHook; if (buildHookArgs.empty()) throw Error("'build-hook' setting is empty"); diff --git a/src/libstore/unix/include/nix/store/build/hook-instance.hh b/src/libstore/unix/include/nix/store/build/hook-instance.hh index d2a73c9a8..e53a791d7 100644 --- a/src/libstore/unix/include/nix/store/build/hook-instance.hh +++ b/src/libstore/unix/include/nix/store/build/hook-instance.hh @@ -58,7 +58,7 @@ struct HookInstance */ std::function onKillChild; - HookInstance(); + HookInstance(const Strings & buildHook); ~HookInstance(); }; diff --git a/src/nix/build-remote/build-remote.cc b/src/nix/build-remote/build-remote.cc index eec0a0f28..408df47c0 100644 --- a/src/nix/build-remote/build-remote.cc +++ b/src/nix/build-remote/build-remote.cc @@ -76,8 +76,8 @@ static int main_build_remote(int argc, char ** argv) settings.set(name, value); } - auto maxBuildJobs = settings.maxBuildJobs; - settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work + auto maxBuildJobs = settings.getWorkerSettings().maxBuildJobs; + settings.getWorkerSettings().maxBuildJobs.set("1"); // hack to make tests with local?root= work initPlugins(); @@ -284,7 +284,7 @@ static int main_build_remote(int argc, char ** argv) signal(SIGALRM, old); } - auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute; + auto substitute = settings.getWorkerSettings().buildersUseSubstitutes ? Substitute : NoSubstitute; { Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri)); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 338ce6cc6..8906628ff 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1,3 +1,4 @@ +#include "nix/cmd/common-eval-args.hh" #include "nix/main/common-args.hh" #include "nix/main/shared.hh" #include "nix/expr/eval.hh" @@ -9,6 +10,7 @@ #include "nix/store/derivations.hh" #include "nix/store/outputs-spec.hh" #include "nix/expr/attr-path.hh" +#include "nix/fetchers/fetch-settings.hh" #include "nix/fetchers/fetchers.hh" #include "nix/fetchers/registry.hh" #include "nix/expr/eval-cache.hh" @@ -129,7 +131,7 @@ public: void run(nix::ref store) override { - settings.tarballTtl = 0; + fetchSettings.tarballTtl = 0; auto updateAll = lockFlags.inputUpdates.empty(); lockFlags.recreateLockFile = updateAll; @@ -162,7 +164,7 @@ struct CmdFlakeLock : FlakeCommand void run(nix::ref store) override { - settings.tarballTtl = 0; + fetchSettings.tarballTtl = 0; lockFlags.writeLockFile = true; lockFlags.failOnUnlocked = true; @@ -364,7 +366,7 @@ struct CmdFlakeCheck : FlakeCommand } catch (Interrupted & e) { throw; } catch (Error & e) { - if (settings.keepGoing) { + if (settings.getWorkerSettings().keepGoing) { logError(e.info()); hasErrors = true; } else diff --git a/src/nix/main.cc b/src/nix/main.cc index e11cb61fd..6f7947515 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -1,3 +1,5 @@ +#include "nix/cmd/common-eval-args.hh" +#include "nix/fetchers/fetch-settings.hh" #include "nix/util/args/root.hh" #include "nix/util/current-process.hh" #include "nix/cmd/command.hh" @@ -384,7 +386,7 @@ void mainWrapped(int argc, char ** argv) self-aware. That is, it has to know where it is installed. We don't think it's sentient. */ - settings.buildHook.setDefault( + settings.getWorkerSettings().buildHook.setDefault( Strings{ getNixBin({}).string(), "__build-remote", @@ -552,10 +554,10 @@ void mainWrapped(int argc, char ** argv) if (!args.useNet) { // FIXME: should check for command line overrides only. - if (!settings.useSubstitutes.overridden) - settings.useSubstitutes = false; - if (!settings.tarballTtl.overridden) - settings.tarballTtl = std::numeric_limits::max(); + if (!settings.getWorkerSettings().useSubstitutes.overridden) + settings.getWorkerSettings().useSubstitutes = false; + if (!fetchSettings.tarballTtl.overridden) + fetchSettings.tarballTtl = std::numeric_limits::max(); if (!fileTransferSettings.tries.overridden) fileTransferSettings.tries = 0; if (!fileTransferSettings.connectTimeout.overridden) @@ -563,7 +565,7 @@ void mainWrapped(int argc, char ** argv) } if (args.refresh) { - settings.tarballTtl = 0; + fetchSettings.tarballTtl = 0; settings.ttlNegativeNarInfoCache = 0; settings.ttlPositiveNarInfoCache = 0; } diff --git a/src/nix/nix-build/nix-build.cc b/src/nix/nix-build/nix-build.cc index e4a9c766f..13c38f929 100644 --- a/src/nix/nix-build/nix-build.cc +++ b/src/nix/nix-build/nix-build.cc @@ -552,7 +552,10 @@ static void main_nix_build(int argc, char ** argv) env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmpDir.path().string(); env["NIX_STORE"] = store->storeDir; - env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores ? settings.buildCores : settings.getDefaultCores()); + env["NIX_BUILD_CORES"] = + fmt("%d", + settings.getLocalSettings().buildCores ? settings.getLocalSettings().buildCores + : settings.getDefaultCores()); DerivationOptions drvOptions; try { diff --git a/src/nix/nix-store/nix-store.cc b/src/nix/nix-store/nix-store.cc index 7acd7c8d0..f9c32ee06 100644 --- a/src/nix/nix-store/nix-store.cc +++ b/src/nix/nix-store/nix-store.cc @@ -898,7 +898,7 @@ static void opServe(Strings opFlags, Strings opArgs) // building through the daemon. verbosity = lvlError; settings.getLogFileSettings().keepLog = false; - settings.useSubstitutes = false; + settings.getWorkerSettings().useSubstitutes = false; auto options = ServeProto::Serialise::read(*store, rconn); @@ -907,10 +907,10 @@ static void opServe(Strings opFlags, Strings opArgs) // See how the serialization logic in // `ServeProto::Serialise` matches // these conditions. - settings.maxSilentTime = options.maxSilentTime; - settings.buildTimeout = options.buildTimeout; + settings.getWorkerSettings().maxSilentTime = options.maxSilentTime; + settings.getWorkerSettings().buildTimeout = options.buildTimeout; if (clientVersion >= ServeProto::Version{2, 2}) - settings.maxLogSize = options.maxLogSize; + settings.getWorkerSettings().maxLogSize = options.maxLogSize; if (clientVersion >= ServeProto::Version{2, 3}) { if (options.nrRepeats != 0) { throw Error("client requested repeating builds, but this is not currently implemented");