diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 2285e0c3cf51..f88978cd256c 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -15,6 +15,7 @@ #include "nix/expr/eval.hh" #include "nix/expr/eval-settings.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/main/shared.hh" #include "nix/flake/flake.hh" #include "nix/expr/eval-cache.hh" @@ -593,7 +594,7 @@ std::vector Installable::build2( if (settings.printMissing) printMissing(store, pathsToBuild, lvlInfo); - auto buildResults = store->buildPathsWithResults(pathsToBuild, bMode, evalStore); + auto buildResults = store->getBuilder(evalStore)->buildPathsWithResults(pathsToBuild, bMode); for (auto & buildResult : buildResults) { if (buildResult.tryGetFailure()) { for (auto & aux : backmap[buildResult.path]) { diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 6eaab847a0a6..06670517b4e4 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -26,6 +26,7 @@ #include "nix/util/finally.hh" #include "nix/cmd/markdown.hh" #include "nix/store/local-fs-store.hh" +#include "nix/store/build.hh" #include "nix/expr/print.hh" #include "nix/util/ref.hh" #include "nix/expr/value.hh" @@ -549,7 +550,7 @@ ProcessLineResult NixRepl::processLine(std::string line) std::string drvPathRaw = state->store->printStorePath(drvPath); if (command == ":b" || command == ":bl") { - state->store->buildPaths({ + state->store->getBuilder()->buildPaths({ DerivedPath::Built{ .drvPath = makeConstantStorePathRef(drvPath), .outputs = OutputsSpec::All{}, diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index cf009515c57c..616845ae9787 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -10,6 +10,7 @@ #include "nix/store/names.hh" #include "nix/store/path-references.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/util/util.hh" #include "nix/util/os-string.hh" #include "nix/util/processes.hh" @@ -131,7 +132,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS buildReqs.reserve(drvs.size()); for (auto & d : drvs) buildReqs.emplace_back(DerivedPath{d}); - buildStore->buildPaths(buildReqs, bmNormal, store); + buildStore->getBuilder(store)->buildPaths(buildReqs, bmNormal); StorePathSet outputsToCopyAndAllow; @@ -2035,7 +2036,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value ** args, V state.error("path '%1%' is not in the Nix store", sourcePath).atPos(pos).debugThrow(); auto storePath = state.store->toStorePath(sourcePath.path.abs()).first; if (!state.storeFS->getMount(CanonPath(state.store->printStorePath(storePath))) && !settings.readOnlyMode) - state.store->ensurePath(storePath); + state.store->getBuilder()->ensurePath(storePath); context.insert(NixStringContextElem::Opaque{.path = storePath}); v.mkString(sourcePath.path.abs(), context, state.mem); } diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 569e8a924d23..51c22b6a0cb6 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -2,6 +2,7 @@ #include "nix/expr/eval-inline.hh" #include "nix/store/derivations.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/store/globals.hh" namespace nix { @@ -293,7 +294,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value ** arg state.error("context key '%s' is not a store path", name).atPos(i.pos).debugThrow(); auto namePath = state.store->parseStorePath(name); if (!settings.readOnlyMode) - state.store->ensurePath(namePath); + state.store->getBuilder()->ensurePath(namePath); state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context"); if (auto attr = i.value->attrs()->get(sPath)) { diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 392294abc7e0..8f09280c6831 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -5,6 +5,7 @@ #include "nix/expr/eval-settings.hh" #include "nix/expr/fetch-tree.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/fetchers/fetchers.hh" #include "nix/store/filetransfer.hh" #include "nix/fetchers/registry.hh" @@ -536,7 +537,7 @@ static void fetch( // Try to get the path from the local store or substituters try { - state.store->ensurePath(expectedPath); + state.store->getBuilder()->ensurePath(expectedPath); debug("using substituted/cached path '%s' for '%s'", state.store->printStorePath(expectedPath), *url); state.allowAndSetStorePathString(expectedPath, v); return; diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index b1bc134ac34e..f11ceb067a42 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -1,6 +1,7 @@ #include "nix/fetchers/fetchers.hh" #include "nix/store/store-api.hh" #include "nix/util/fs-sink.hh" +#include "nix/store/build.hh" #include "nix/util/source-path.hh" #include "nix/fetchers/fetch-to-store.hh" #include "nix/util/json-utils.hh" @@ -382,7 +383,7 @@ std::pair, Input> Input::getAccessorUnchecked(const Settings /* If not, try to substitute the input. */ if (storePath) { try { - store.ensurePath(*storePath); + store.getBuilder()->ensurePath(*storePath); return makeStoreAccessor(); } // Ignore any substitution error. diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index f0d8aeab3aa1..26e09d53259c 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -8,6 +8,7 @@ #include "nix/store/path.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/store/store-open.hh" #include "nix/store/store-reference.hh" #include "nix/store/build-result.hh" @@ -178,7 +179,7 @@ nix_err nix_store_realise( .drvPath = nix::makeConstantStorePathRef(path->path), .outputs = nix::OutputsSpec::All{}}}; const auto nixStore = store->ptr; - auto results = nixStore->buildPathsWithResults(paths, nix::bmNormal, nixStore); + auto results = nixStore->getBuilder(nixStore)->buildPathsWithResults(paths, nix::bmNormal); assert(results.size() == 1); @@ -420,7 +421,7 @@ nix_err nix_store_build_paths( derived_paths.push_back(nix::SingleDerivedPath::Opaque{store_path->path}); } - auto results = store->ptr->buildPathsWithResults(derived_paths); + auto results = store->ptr->getBuilder()->buildPathsWithResults(derived_paths); for (auto & result : results) { if (callback) callback( diff --git a/src/libstore/build/derivation-building-goal.cc b/src/libstore/build/derivation-building-goal.cc index 19f1cbadcc55..0c094c713fac 100644 --- a/src/libstore/build/derivation-building-goal.cc +++ b/src/libstore/build/derivation-building-goal.cc @@ -1,5 +1,7 @@ #include "nix/store/build/derivation-building-goal.hh" #include "nix/store/build/derivation-env-desugar.hh" +#include "nix/store/restricted-store.hh" +#include "nix/store/daemon.hh" #ifndef _WIN32 // TODO enable build hook on Windows # include "nix/store/build/hook-instance.hh" # include "nix/store/build/derivation-builder.hh" @@ -946,6 +948,22 @@ Goal::Co DerivationBuildingGoal::buildLocally( { closeLogFileFn(); } + + void processDaemonConnection( + ref store, FdSource && from, FdSink && to, RestrictionContext & context) override + { + /** + * TODO: We create a fresh Worker here because the + * parent Worker is blocked waiting for the current + * build to finish, so we can't reuse it from a + * daemon thread. Ideally we should reuse the same + * Worker to share scheduling state. + */ + Worker freshWorker{goal.worker.store, goal.worker.evalStore}; + auto builder = makeRestrictedBuilder(freshWorker, context); + daemon::processConnection( + store, std::move(from), std::move(to), NotTrusted, daemon::Recursive, builder.get_ptr()); + } }; decltype(DerivationBuilderParams::defaultPathsInChroot) defaultPathsInChroot = diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc index dbc688cb033b..069196ca3915 100644 --- a/src/libstore/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -3,18 +3,47 @@ #include "nix/store/build/substitution-goal.hh" #include "nix/store/build/derivation-trampoline-goal.hh" #include "nix/util/strings.hh" +#include namespace nix { -void Store::buildPaths(const std::vector & reqs, BuildMode buildMode, std::shared_ptr evalStore) +void LocalBuilder::buildPaths(const std::vector & reqs, BuildMode buildMode) { - Worker worker(*this, evalStore ? *evalStore : *this); + getWorker()->buildPaths(reqs, buildMode); +} + +std::vector +LocalBuilder::buildPathsWithResults(const std::vector & reqs, BuildMode buildMode) +{ + return getWorker()->buildPathsWithResults(reqs, buildMode); +} + +BuildResult LocalBuilder::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) +{ + return getWorker()->buildDerivation(drvPath, drv, buildMode); +} + +void LocalBuilder::ensurePath(const StorePath & path) +{ + /* If the path is already valid, we're done. */ + if (store->isValidPath(path)) + return; + getWorker()->ensurePath(path); +} + +void LocalBuilder::repairPath(const StorePath & path) +{ + getWorker()->repairPath(path); +} + +void Worker::buildPaths(const std::vector & reqs, BuildMode buildMode) +{ Goals goals; for (auto & br : reqs) - goals.insert(worker.makeGoal(br, buildMode)); + goals.insert(makeGoal(br, buildMode)); - worker.run(goals); + run(goals); StringSet failed; BuildResult::Failure * failure = nullptr; @@ -27,38 +56,35 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod } if (i->exitCode != Goal::ecSuccess) { if (auto i2 = dynamic_cast(i.get())) - failed.insert(i2->drvReq->to_string(*this)); + failed.insert(i2->drvReq->to_string(store)); else if (auto i2 = dynamic_cast(i.get())) - failed.insert(printStorePath(i2->storePath)); + failed.insert(store.printStorePath(i2->storePath)); } } if (failed.size() == 1 && failure) { - failure->withExitStatus(worker.exitStatusFlags.failingExitStatus()); + failure->withExitStatus(exitStatusFlags.failingExitStatus()); throw *failure; } else if (!failed.empty()) { - auto exitStatus = worker.exitStatusFlags.failingExitStatus(); + auto exitStatus = exitStatusFlags.failingExitStatus(); if (failure) logError(failure->info()); throw Error(exitStatus, "build of %s failed", concatStringsSep(", ", quoteStrings(failed))); } } -std::vector Store::buildPathsWithResults( - const std::vector & reqs, BuildMode buildMode, std::shared_ptr evalStore) +std::vector Worker::buildPathsWithResults(const std::vector & reqs, BuildMode buildMode) { - Worker worker(*this, evalStore ? *evalStore : *this); - Goals goals; std::vector> state; for (const auto & req : reqs) { - auto goal = worker.makeGoal(req, buildMode); + auto goal = makeGoal(req, buildMode); goals.insert(goal); state.push_back({req, goal}); } - worker.run(goals); + run(goals); std::vector results; results.reserve(state.size()); @@ -74,13 +100,12 @@ std::vector Store::buildPathsWithResults( return results; } -BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) +BuildResult Worker::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) { - Worker worker(*this, *this); - auto goal = worker.makeDerivationTrampolineGoal(drvPath, OutputsSpec::All{}, drv, buildMode); + auto goal = makeDerivationTrampolineGoal(drvPath, OutputsSpec::All{}, drv, buildMode); try { - worker.run(Goals{goal}); + run(Goals{goal}); return goal->buildResult; } catch (Error & e) { return BuildResult{ @@ -91,49 +116,47 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat }; } -void Store::ensurePath(const StorePath & path) +void Worker::ensurePath(const StorePath & path) { /* If the path is already valid, we're done. */ - if (isValidPath(path)) + if (store.isValidPath(path)) return; - Worker worker(*this, *this); - GoalPtr goal = worker.makePathSubstitutionGoal(path, true); + GoalPtr goal = makePathSubstitutionGoal(path, true); Goals goals = {goal}; - worker.run(goals); + run(goals); if (goal->exitCode != Goal::ecSuccess) { - auto exitStatus = worker.exitStatusFlags.failingExitStatus(); + auto exitStatus = exitStatusFlags.failingExitStatus(); goal->buildResult.tryThrowBuildError(exitStatus); - throw Error(exitStatus, "path '%s' does not exist and cannot be created", printStorePath(path)); + throw Error(exitStatus, "path '%s' does not exist and cannot be created", store.printStorePath(path)); } } -void Store::repairPath(const StorePath & path) +void Worker::repairPath(const StorePath & path) { - Worker worker(*this, *this); - GoalPtr goal = worker.makePathSubstitutionGoal(path, true, Repair); + GoalPtr goal = makePathSubstitutionGoal(path, true, Repair); Goals goals = {goal}; - worker.run(goals); + run(goals); if (goal->exitCode != Goal::ecSuccess) { /* Since substituting the path didn't work, if we have a valid deriver, then rebuild the deriver. */ - auto info = queryPathInfo(path); - if (info->deriver && isValidPath(*info->deriver)) { + auto info = store.queryPathInfo(path); + if (info->deriver && store.isValidPath(*info->deriver)) { goals.clear(); - goals.insert(worker.makeGoal( + goals.insert(makeGoal( DerivedPath::Built{ .drvPath = makeConstantStorePathRef(*info->deriver), // FIXME: Should just build the specific output we need. .outputs = OutputsSpec::All{}, }, bmRepair)); - worker.run(goals); + run(goals); } else - throw Error(worker.exitStatusFlags.failingExitStatus(), "cannot repair path '%s'", printStorePath(path)); + throw Error(exitStatusFlags.failingExitStatus(), "cannot repair path '%s'", store.printStorePath(path)); } } diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 534b54b3a410..6a0bde6dcc12 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -3,6 +3,7 @@ #include "nix/store/worker-protocol.hh" #include "nix/store/worker-protocol-connection.hh" #include "nix/store/worker-protocol-impl.hh" +#include "nix/store/build.hh" #include "nix/store/store-api.hh" #include "nix/store/store-cast.hh" #include "nix/store/filetransfer.hh" @@ -312,7 +313,8 @@ static void performOp( TrustedFlag trusted, RecursiveFlag recursive, WorkerProto::BasicServerConnection & conn, - WorkerProto::Op op) + WorkerProto::Op op, + Builder & builder) { WorkerProto::ReadConn rconn(conn); WorkerProto::WriteConn wconn(conn); @@ -564,7 +566,7 @@ static void performOp( if (mode == bmRepair && !trusted) throw Error("repairing is not allowed because you are not in 'trusted-users'"); logger->startWork(); - store->buildPaths(drvs, mode); + builder.buildPaths(drvs, mode); logger->stopWork(); conn.to << 1; break; @@ -583,7 +585,7 @@ static void performOp( throw Error("repairing is not allowed because you are not in 'trusted-users'"); logger->startWork(); - auto results = store->buildPathsWithResults(drvs, mode); + auto results = builder.buildPathsWithResults(drvs, mode); logger->stopWork(); WorkerProto::write(*store, wconn, results); @@ -662,7 +664,7 @@ static void performOp( drvPath = store->writeDerivation(Derivation{drv2}); } - auto res = store->buildDerivation(drvPath, drv, buildMode); + auto res = builder.buildDerivation(drvPath, drv, buildMode); logger->stopWork(); WorkerProto::write(*store, wconn, res); break; @@ -671,7 +673,7 @@ static void performOp( case WorkerProto::Op::EnsurePath: { auto path = WorkerProto::Serialise::read(*store, rconn); logger->startWork(); - store->ensurePath(path); + builder.ensurePath(path); logger->stopWork(); conn.to << 1; break; @@ -1083,7 +1085,13 @@ static void performOp( } } -void processConnection(ref store, FdSource && from, FdSink && to, TrustedFlag trusted, RecursiveFlag recursive) +void processConnection( + ref store, + FdSource && from, + FdSink && to, + TrustedFlag trusted, + RecursiveFlag recursive, + std::shared_ptr builder) { #ifndef _WIN32 // TODO need graceful async exit support on Windows? auto monitor = !recursive ? std::make_unique(from.fd) : nullptr; @@ -1101,6 +1109,9 @@ void processConnection(ref store, FdSource && from, FdSink && to, Trusted }); #endif + if (!builder) + builder = store->getBuilder(); + /* Exchange the greeting. */ auto localVersion = WorkerProto::latest; if (recursive) @@ -1167,7 +1178,7 @@ void processConnection(ref store, FdSource && from, FdSink && to, Trusted debug("performing daemon worker op: %d", op); try { - performOp(tunnelLogger, store, trusted, recursive, conn, op); + performOp(tunnelLogger, store, trusted, recursive, conn, op, *builder); } catch (Error & e) { /* If we're not in a state where we can send replies, then something went wrong processing the input of the diff --git a/src/libstore/include/nix/store/build.hh b/src/libstore/include/nix/store/build.hh new file mode 100644 index 000000000000..dbf2fc76ce7b --- /dev/null +++ b/src/libstore/include/nix/store/build.hh @@ -0,0 +1,97 @@ +#pragma once +///@file + +#include "nix/store/store-api.hh" + +namespace nix { + +/** + * Abstract interface for the build scheduler entry points. + * + * `Worker` implements this for local scheduling, including local builds. + * Remote stores provide a `Builder` via `Store::getBuilder()`. + * + * Thread safety should be guaranteed across these methods. + */ +struct Builder +{ + /* VTable anchor to avoid weak linkage of the vtable - it breaks + dynamic_cast across shared libraries on Darwin. */ + virtual void anchor(); + + /** + * For each path, if it's a derivation, build it. Building a + * derivation means ensuring that the output paths are valid. If + * they are already valid, this is a no-op. Otherwise, validity + * can be reached in two ways. First, if the output paths is + * substitutable, then build the path that way. Second, the + * output paths can be created by running the builder, after + * recursively building any sub-derivations. For inputs that are + * not derivations, substitute them. + */ + virtual void buildPaths(const std::vector & reqs, BuildMode buildMode = bmNormal) = 0; + + /** + * Like buildPaths(), but return a vector of \ref BuildResult + * BuildResults corresponding to each element in paths. Note that in + * case of a build/substitution error, this function won't throw an + * exception, but return a BuildResult containing an error message. + */ + virtual std::vector + buildPathsWithResults(const std::vector & reqs, BuildMode buildMode = bmNormal) = 0; + + /** + * Build a single non-materialized derivation (i.e. not from an + * on-disk .drv file). + * + * @param drvPath This is used to deduplicate worker goals so it is + * imperative that is correct. That said, it doesn't literally need + * to be store path that would be calculated from writing this + * derivation to the store: it is OK if it instead is that of a + * Derivation which would resolve to this (by taking the outputs of + * it's input derivations and adding them as input sources) such + * that the build time referenceable-paths are the same. + * + * In the input-addressed case, we usually *do* use an "original" + * unresolved derivations's path, as that is what will be used in the + * buildPaths case. Also, the input-addressed output paths are verified + * only by that contents of that specific unresolved derivation, so it is + * nice to keep that information around so if the original derivation is + * ever obtained later, it can be verified whether the trusted user in fact + * used the proper output path. + * + * In the content-addressed case, we want to always use the resolved + * drv path calculated from the provided derivation. This serves two + * purposes: + * + * - It keeps the operation trustless, by ruling out a maliciously + * invalid drv path corresponding to a non-resolution-equivalent + * derivation. + * + * - For the floating case in particular, it ensures that the derivation + * to output mapping respects the resolution equivalence relation, so + * one cannot choose different resolution-equivalent derivations to + * subvert dependency coherence (i.e. the property that one doesn't end + * up with multiple different versions of dependencies without + * explicitly choosing to allow it). + */ + virtual BuildResult + buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode = bmNormal) = 0; + + /** + * Ensure that a path is valid. If it is not currently valid, it + * may be made valid by running a substitute (if defined for the + * path). + */ + virtual void ensurePath(const StorePath & path) = 0; + + /** + * Repair the contents of the given path by redownloading it using + * a substituter (if available). + */ + virtual void repairPath(const StorePath & path) = 0; + + virtual ~Builder() = default; +}; + +} // namespace nix diff --git a/src/libstore/include/nix/store/build/derivation-builder.hh b/src/libstore/include/nix/store/build/derivation-builder.hh index 2562e29da743..fcb25b6bfe40 100644 --- a/src/libstore/include/nix/store/build/derivation-builder.hh +++ b/src/libstore/include/nix/store/build/derivation-builder.hh @@ -150,6 +150,13 @@ struct DerivationBuilderCallbacks * @todo this should be reworked */ virtual void childTerminated() = 0; + + /** + * Process a recursive Nix daemon connection, using a builder + * that enforces the restrictions of the given context. + */ + virtual void + processDaemonConnection(ref store, FdSource && from, FdSink && to, RestrictionContext & context) = 0; }; /** diff --git a/src/libstore/include/nix/store/build/worker.hh b/src/libstore/include/nix/store/build/worker.hh index 8bb35221e72a..cd0010d201a9 100644 --- a/src/libstore/include/nix/store/build/worker.hh +++ b/src/libstore/include/nix/store/build/worker.hh @@ -3,6 +3,7 @@ #include "nix/util/types.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/store/derived-path-map.hh" #include "nix/store/build/goal.hh" #include "nix/store/build-result.hh" @@ -67,10 +68,44 @@ struct Child struct HookInstance; #endif +/** + * Owns a worker. Optimization around ensurePath to prevent a Worker from + * being constructed when it's not needed. + */ +class LocalBuilder : public Builder +{ +public: + LocalBuilder(ref store, ref evalStore) + : store(store) + , evalStore(evalStore) {}; + + /* Builder interface — see `Builder` for documentation. */ + + void buildPaths(const std::vector & reqs, BuildMode buildMode) override; + std::vector + buildPathsWithResults(const std::vector & reqs, BuildMode buildMode) override; + BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) override; + void ensurePath(const StorePath & path) override; + void repairPath(const StorePath & path) override; + +private: + /** + * Intentionally construct a new worker for each operation, to avoid + * reusing a worker between calls, allowing for thread safety. + */ + inline std::shared_ptr getWorker() + { + return std::make_shared(*store, *evalStore); + } + + ref store; + ref evalStore; +}; + /** * Coordinates one or more realisations and their interdependencies. */ -class Worker +class Worker : public Builder { private: @@ -199,6 +234,7 @@ public: Store & store; Store & evalStore; + const WorkerSettings & settings; /** @@ -390,6 +426,15 @@ public: act.setExpected(actFileTransfer, expectedDownloadSize + doneDownloadSize); act.setExpected(actCopyPath, expectedNarSize + doneNarSize); } + + /* Builder interface — see `Builder` for documentation. */ + + void buildPaths(const std::vector & reqs, BuildMode buildMode) override; + std::vector + buildPathsWithResults(const std::vector & reqs, BuildMode buildMode) override; + BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) override; + void ensurePath(const StorePath & path) override; + void repairPath(const StorePath & path) override; }; } // namespace nix diff --git a/src/libstore/include/nix/store/daemon.hh b/src/libstore/include/nix/store/daemon.hh index 4d550696e877..a01205a976fd 100644 --- a/src/libstore/include/nix/store/daemon.hh +++ b/src/libstore/include/nix/store/daemon.hh @@ -4,10 +4,22 @@ #include "nix/util/serialise.hh" #include "nix/store/store-api.hh" -namespace nix::daemon { +namespace nix { + +struct Builder; + +namespace daemon { enum RecursiveFlag : bool { NotRecursive = false, Recursive = true }; -void processConnection(ref store, FdSource && from, FdSink && to, TrustedFlag trusted, RecursiveFlag recursive); +void processConnection( + ref store, + FdSource && from, + FdSink && to, + TrustedFlag trusted, + RecursiveFlag recursive, + std::shared_ptr builder = nullptr); + +} // namespace daemon -} // namespace nix::daemon +} // namespace nix diff --git a/src/libstore/include/nix/store/legacy-ssh-store.hh b/src/libstore/include/nix/store/legacy-ssh-store.hh index 40351014bf43..5349275ae24a 100644 --- a/src/libstore/include/nix/store/legacy-ssh-store.hh +++ b/src/libstore/include/nix/store/legacy-ssh-store.hh @@ -2,7 +2,6 @@ ///@file #include "nix/store/common-ssh-store-config.hh" -#include "nix/store/store-api.hh" #include "nix/store/ssh.hh" #include "nix/util/callback.hh" #include "nix/util/pool.hh" @@ -146,24 +145,7 @@ public: public: - BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) override; - - /** - * Note, the returned function must only be called once, or we'll - * try to read from the connection twice. - * - * @todo Use C++23 `std::move_only_function`. - */ - fun buildDerivationAsync( - const StorePath & drvPath, const BasicDerivation & drv, const ServeProto::BuildOptions & options); - - void buildPaths( - const std::vector & drvPaths, BuildMode buildMode, std::shared_ptr evalStore) override; - - void ensurePath(const StorePath & path) override - { - unsupported("ensurePath"); - } + ref getBuilder(std::shared_ptr evalStore) override; ref getFSAccessor(bool requireValidPath) override { @@ -175,19 +157,6 @@ public: unsupported("getFSAccessor"); } - /** - * The default instance would schedule the work on the client side, but - * for consistency with `buildPaths` and `buildDerivation` it should happen - * on the remote side. - * - * We make this fail for now so we can add implement this properly later - * without it being a breaking change. - */ - void repairPath(const StorePath & path) override - { - unsupported("repairPath"); - } - void computeFSClosure( const StorePathSet & paths, StorePathSet & out, @@ -238,6 +207,8 @@ public: // not supported return {}; } + + friend struct LegacySSHBuilder; }; } // namespace nix diff --git a/src/libstore/include/nix/store/machines.hh b/src/libstore/include/nix/store/machines.hh index a3e9353c80fa..50d5350838b0 100644 --- a/src/libstore/include/nix/store/machines.hh +++ b/src/libstore/include/nix/store/machines.hh @@ -16,6 +16,13 @@ struct Machine { const StoreReference storeUri; + /** + * @TODO this information should eventually just exist to update an + * underlying setting on `Store::Config`, just as the feature information + * updates `Store::Config::systemType`. The only wrinkle is whether the + * makes sense for separate local stores to have distinct systems, when they + * are all the current OS, just different part of the file system. + */ const StringSet systemTypes; const std::optional sshKey; const unsigned int maxJobs; diff --git a/src/libstore/include/nix/store/meson.build b/src/libstore/include/nix/store/meson.build index 132a9067558e..a2563806f523 100644 --- a/src/libstore/include/nix/store/meson.build +++ b/src/libstore/include/nix/store/meson.build @@ -15,6 +15,7 @@ headers = [ config_pub_h ] + files( 'aws-creds.hh', 'binary-cache-store.hh', 'build-result.hh', + 'build.hh', 'build/build-log.hh', 'build/derivation-builder.hh', 'build/derivation-building-goal.hh', diff --git a/src/libstore/include/nix/store/remote-store.hh b/src/libstore/include/nix/store/remote-store.hh index 0769afd6b34b..0db0eb155c79 100644 --- a/src/libstore/include/nix/store/remote-store.hh +++ b/src/libstore/include/nix/store/remote-store.hh @@ -126,15 +126,7 @@ public: void queryRealisationUncached( const DrvOutput &, Callback> callback) noexcept override; - void - buildPaths(const std::vector & paths, BuildMode buildMode, std::shared_ptr evalStore) override; - - std::vector buildPathsWithResults( - const std::vector & paths, BuildMode buildMode, std::shared_ptr evalStore) override; - - BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) override; - - void ensurePath(const StorePath & path) override; + ref getBuilder(std::shared_ptr evalStore) override; void addTempRoots(const StorePathSet & paths, bool skipIfSlow) override; @@ -146,19 +138,6 @@ public: bool verifyStore(bool checkContents, RepairFlag repair) override; - /** - * The default instance would schedule the work on the client side, but - * for consistency with `buildPaths` and `buildDerivation` it should happen - * on the remote side. - * - * We make this fail for now so we can add implement this properly later - * without it being a breaking change. - */ - void repairPath(const StorePath & path) override - { - unsupported("repairPath"); - } - void addSignatures(const StorePath & storePath, const std::set & sigs) override; MissingPaths queryMissing(const std::vector & targets) override; @@ -228,7 +207,7 @@ private: */ Sync> connectionFds; - void copyDrvsFromEvalStore(const std::vector & paths, std::shared_ptr evalStore); + friend struct RemoteBuilder; }; } // namespace nix diff --git a/src/libstore/include/nix/store/restricted-store.hh b/src/libstore/include/nix/store/restricted-store.hh index aad0a9695104..5b6e8b734c8e 100644 --- a/src/libstore/include/nix/store/restricted-store.hh +++ b/src/libstore/include/nix/store/restricted-store.hh @@ -8,8 +8,10 @@ namespace nix { +struct Builder; class LocalStore; struct LocalStoreConfig; +class Worker; /** * A restricted store has a pointer to one of these, which manages the @@ -116,4 +118,10 @@ protected: */ ref makeRestrictedStore(ref config, ref next, RestrictionContext & context); +/** + * Create a builder that wraps an inner builder, adding restriction + * checks and dependency tracking for recursive Nix builds. + */ +ref makeRestrictedBuilder(Worker & inner, RestrictionContext & context); + } // namespace nix diff --git a/src/libstore/include/nix/store/store-api.hh b/src/libstore/include/nix/store/store-api.hh index 78a819c21540..b22674a95fab 100644 --- a/src/libstore/include/nix/store/store-api.hh +++ b/src/libstore/include/nix/store/store-api.hh @@ -61,6 +61,8 @@ enum TrustedFlag : bool { NotTrusted = false, Trusted = true }; struct BuildResult; struct KeyedBuildResult; +struct Builder; + typedef std::map> StorePathCAMap; /** @@ -459,6 +461,15 @@ public: virtual ~Store() {} + /** + * Get a `Builder` for this store. + * + * @param evalStore If provided and different from this store, + * derivation files will be copied from the eval store to this + * store before building. + */ + virtual ref getBuilder(std::shared_ptr evalStore = nullptr); + /** * Follow symlinks until we end up with a path in the Nix store. */ @@ -487,6 +498,8 @@ public: * If requested, substitute missing paths. This * implements nix-copy-closure's --use-substitutes * flag. + * + * @TODO suspicious to have a Store method that uses `getBuilder`. */ void substitutePaths(const StorePathSet & paths); @@ -770,77 +783,6 @@ public: */ virtual void narFromPath(const StorePath & path, Sink & sink); - /** - * For each path, if it's a derivation, build it. Building a - * derivation means ensuring that the output paths are valid. If - * they are already valid, this is a no-op. Otherwise, validity - * can be reached in two ways. First, if the output paths is - * substitutable, then build the path that way. Second, the - * output paths can be created by running the builder, after - * recursively building any sub-derivations. For inputs that are - * not derivations, substitute them. - */ - virtual void buildPaths( - const std::vector & paths, - BuildMode buildMode = bmNormal, - std::shared_ptr evalStore = nullptr); - - /** - * Like buildPaths(), but return a vector of \ref BuildResult - * BuildResults corresponding to each element in paths. Note that in - * case of a build/substitution error, this function won't throw an - * exception, but return a BuildResult containing an error message. - */ - virtual std::vector buildPathsWithResults( - const std::vector & paths, - BuildMode buildMode = bmNormal, - std::shared_ptr evalStore = nullptr); - - /** - * Build a single non-materialized derivation (i.e. not from an - * on-disk .drv file). - * - * @param drvPath This is used to deduplicate worker goals so it is - * imperative that is correct. That said, it doesn't literally need - * to be store path that would be calculated from writing this - * derivation to the store: it is OK if it instead is that of a - * Derivation which would resolve to this (by taking the outputs of - * it's input derivations and adding them as input sources) such - * that the build time referenceable-paths are the same. - * - * In the input-addressed case, we usually *do* use an "original" - * unresolved derivations's path, as that is what will be used in the - * buildPaths case. Also, the input-addressed output paths are verified - * only by that contents of that specific unresolved derivation, so it is - * nice to keep that information around so if the original derivation is - * ever obtained later, it can be verified whether the trusted user in fact - * used the proper output path. - * - * In the content-addressed case, we want to always use the resolved - * drv path calculated from the provided derivation. This serves two - * purposes: - * - * - It keeps the operation trustless, by ruling out a maliciously - * invalid drv path corresponding to a non-resolution-equivalent - * derivation. - * - * - For the floating case in particular, it ensures that the derivation - * to output mapping respects the resolution equivalence relation, so - * one cannot choose different resolution-equivalent derivations to - * subvert dependency coherence (i.e. the property that one doesn't end - * up with multiple different versions of dependencies without - * explicitly choosing to allow it). - */ - virtual BuildResult - buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode = bmNormal); - - /** - * Ensure that a path is valid. If it is not currently valid, it - * may be made valid by running a substitute (if defined for the - * path). - */ - virtual void ensurePath(const StorePath & path); - /** * Add a store path as a temporary root of the garbage collector. * The root disappears as soon as we exit. @@ -956,12 +898,6 @@ public: return ref{accessor}; } - /** - * Repair the contents of the given path by redownloading it using - * a substituter (if available). - */ - virtual void repairPath(const StorePath & path); - /** * Add signatures to the specified store path. The signatures are * not verified. @@ -984,6 +920,8 @@ public: /** * Read a derivation, after ensuring its existence through * ensurePath(). + * + * @TODO suspicious to have a Store method that uses `getBuilder`. */ Derivation derivationFromPath(const StorePath & drvPath); diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index cbcc42dbed75..1765c5af747a 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -12,12 +12,61 @@ #include "nix/store/path-with-outputs.hh" #include "nix/store/ssh.hh" #include "nix/store/derivations.hh" +#include "nix/store/build.hh" #include "nix/util/callback.hh" #include "nix/store/store-registration.hh" #include "nix/store/globals.hh" namespace nix { +struct LegacySSHBuilder : Builder +{ + ref store; + + LegacySSHBuilder(ref store) + : store(store) + { + } + +private: + + [[noreturn]] void unsupported(const std::string & op) + { + throw Unsupported("operation '%s' is not supported by store '%s'", op, store->config->getHumanReadableURI()); + } + + std::variant + buildPathsRaw(const std::vector & reqs, BuildMode buildMode); + +public: + + void buildPaths(const std::vector & reqs, BuildMode buildMode) override; + + std::vector + buildPathsWithResults(const std::vector & reqs, BuildMode buildMode) override; + + BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) override; + + /** + * Note, the returned function must only be called once, or we'll + * try to read from the connection twice. + * + * @todo Use C++23 `std::move_only_function`. + */ + fun buildDerivationAsync( + const StorePath & drvPath, const BasicDerivation & drv, const ServeProto::BuildOptions & options); + + void ensurePath(const StorePath & path) override + { + unsupported("ensurePath"); + } + + void repairPath(const StorePath & path) override + { + unsupported("repairPath"); + } +}; + LegacySSHStoreConfig::LegacySSHStoreConfig(const ParsedURL::Authority & authority, const Params & params) : StoreConfig(params, FilePathType::Unix) , CommonSSHStoreConfig(authority, params) @@ -196,32 +245,30 @@ static ServeProto::BuildOptions buildSettings() }; } -BuildResult LegacySSHStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) +BuildResult +LegacySSHBuilder::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) { - auto conn(connections->get()); + auto conn(store->connections->get()); - conn->putBuildDerivationRequest(*this, drvPath, drv, buildSettings()); + conn->putBuildDerivationRequest(*store, drvPath, drv, buildSettings()); - return conn->getBuildDerivationResponse(*this); + return conn->getBuildDerivationResponse(*store); } -fun LegacySSHStore::buildDerivationAsync( +fun LegacySSHBuilder::buildDerivationAsync( const StorePath & drvPath, const BasicDerivation & drv, const ServeProto::BuildOptions & options) { // Until we have C++23 std::move_only_function - auto conn = std::make_shared::Handle>(connections->get()); - (*conn)->putBuildDerivationRequest(*this, drvPath, drv, options); + auto conn = std::make_shared::Handle>(store->connections->get()); + (*conn)->putBuildDerivationRequest(*store, drvPath, drv, options); - return [this, conn]() -> BuildResult { return (*conn)->getBuildDerivationResponse(*this); }; + return [store = this->store, conn]() -> BuildResult { return (*conn)->getBuildDerivationResponse(*store); }; } -void LegacySSHStore::buildPaths( - const std::vector & drvPaths, BuildMode buildMode, std::shared_ptr evalStore) +std::variant +LegacySSHBuilder::buildPathsRaw(const std::vector & drvPaths, BuildMode buildMode) { - if (evalStore && evalStore.get() != this) - throw Error("building on an SSH store is incompatible with '--eval-store'"); - - auto conn(connections->get()); + auto conn(store->connections->get()); conn->to << ServeProto::Command::BuildPaths; Strings ss; @@ -229,11 +276,11 @@ void LegacySSHStore::buildPaths( auto sOrDrvPath = StorePathWithOutputs::tryFromDerivedPath(p); std::visit( overloaded{ - [&](const StorePathWithOutputs & s) { ss.push_back(s.to_string(*this)); }, + [&](const StorePathWithOutputs & s) { ss.push_back(s.to_string(*store)); }, [&](const StorePath & drvPath) { throw Error( "wanted to fetch '%s' but the legacy ssh protocol doesn't support merely substituting drv files via the build paths command. It would build them instead. Try using ssh-ng://", - printStorePath(drvPath)); + store->printStorePath(drvPath)); }, [&](std::monostate) { throw Error( @@ -244,16 +291,108 @@ void LegacySSHStore::buildPaths( } conn->to << ss; - ServeProto::write(*this, *conn, buildSettings()); + ServeProto::write(*store, *conn, buildSettings()); conn->to.flush(); - auto status = CommonProto::Serialise::read(*this, {conn->from}); - if (auto * failure = std::get_if(&status)) { - std::string errorMsg; - conn->from >> errorMsg; - throw BuildError(*failure, std::move(errorMsg)); + auto status = CommonProto::Serialise::read(*store, {conn->from}); + return std::visit( + overloaded{ + [&](BuildResultSuccessStatus s) -> std::variant { return s; }, + [&](BuildResultFailureStatus s) -> std::variant { + std::string errorMsg; + conn->from >> errorMsg; + return BuildError{s, std::move(errorMsg)}; + }, + }, + status); +} + +void LegacySSHBuilder::buildPaths(const std::vector & drvPaths, BuildMode buildMode) +{ + auto status = buildPathsRaw(drvPaths, buildMode); + if (auto * failure = std::get_if(&status)) + throw *failure; +} + +std::vector +LegacySSHBuilder::buildPathsWithResults(const std::vector & reqs, BuildMode buildMode) +{ + auto status = buildPathsRaw(reqs, buildMode); + + std::vector results; + + // N.B. This logic is inspired by the fallback old protocol code in + // `RemoteBuilder::buildPathsWithResults`, which handles a very + // similar problem. + for (auto & req : reqs) { + std::visit( + overloaded{ + [&](const DerivedPath::Opaque & bo) { + results.push_back( + KeyedBuildResult{ + {.inner = std::visit( + overloaded{ + [](const BuildResultSuccessStatus & s) -> decltype(BuildResult::inner) { + return BuildResult::Success{.status = s}; + }, + [](const BuildError & e) -> decltype(BuildResult::inner) { return e; }, + }, + status)}, + /* .path = */ req, + }); + }, + [&](const DerivedPath::Built & bfd) { + if (auto * failure = std::get_if(&status)) { + results.push_back( + KeyedBuildResult{ + {.inner = *failure}, + /* .path = */ req, + }); + return; + } + + BuildResult::Success success{ + .status = std::get(status), + }; + + auto drvPath = resolveDerivedPath(*store, *bfd.drvPath); + auto built = resolveDerivedPath(*store, bfd); + for (auto & [output, outputPath] : built) { + auto outputId = DrvOutput{drvPath, output}; + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { + auto realisation = store->queryRealisation(outputId); + if (!realisation) + throw MissingRealisation(*store, outputId); + success.builtOutputs.emplace(output, *realisation); + } else { + success.builtOutputs.emplace( + output, + UnkeyedRealisation{ + .outPath = outputPath, + }); + } + } + + results.push_back( + KeyedBuildResult{ + {.inner = std::move(success)}, + /* .path = */ req, + }); + }, + }, + req.raw()); } + + return results; +} + +ref LegacySSHStore::getBuilder(std::shared_ptr evalStore) +{ + if (evalStore && evalStore.get() != this) + throw Error("building on an SSH store is incompatible with '--eval-store'"); + return make_ref( + ref(std::dynamic_pointer_cast(shared_from_this()))); } void LegacySSHStore::computeFSClosure( diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index d54902dc7510..3be7ae538776 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1,4 +1,5 @@ #include "nix/store/local-store.hh" +#include "nix/store/build.hh" #include "nix/store/globals.hh" #include "nix/util/git.hh" #include "nix/util/archive.hh" @@ -1661,7 +1662,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) info->narHash.to_string(HashFormat::Nix32, true), current.hash.to_string(HashFormat::Nix32, true)); if (repair) - repairPath(i); + getBuilder()->repairPath(i); else errors = true; } else { @@ -1775,7 +1776,7 @@ void LocalStore::verifyPath( printError("path '%s' disappeared, but it still has valid referrers!", pathS); if (repair) try { - repairPath(path); + getBuilder()->repairPath(path); } catch (Error & e) { logWarning(e.info()); errors = true; diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 4e2a35a04623..9ba8d8bb0a29 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -453,33 +453,6 @@ StorePath resolveDerivedPath(Store & store, const SingleDerivedPath & req, Store req.raw()); } -OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd) -{ - auto drvPath = resolveDerivedPath(store, *bfd.drvPath); - auto outputMap = deepQueryDerivationOutputMap(store, drvPath); - auto outputsLeft = std::visit( - overloaded{ - [&](const OutputsSpec::All &) { return StringSet{}; }, - [&](const OutputsSpec::Names & names) { return static_cast(names); }, - }, - bfd.outputs.raw); - for (auto iter = outputMap.begin(); iter != outputMap.end();) { - auto & outputName = iter->first; - if (bfd.outputs.contains(outputName)) { - outputsLeft.erase(outputName); - ++iter; - } else { - iter = outputMap.erase(iter); - } - } - if (!outputsLeft.empty()) - throw Error( - "derivation '%s' does not have an outputs %s", - store.printStorePath(drvPath), - concatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); - return outputMap; -} - } // namespace nix namespace nlohmann { diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 956a15eb5789..66fc8e1a0ba3 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -1,3 +1,4 @@ +#include "nix/store/build.hh" #include "nix/store/path.hh" #include "nix/store/store-api.hh" #include "nix/util/serialise.hh" @@ -624,60 +625,95 @@ void RemoteStore::queryRealisationUncached( } } -void RemoteStore::copyDrvsFromEvalStore(const std::vector & paths, std::shared_ptr evalStore) +struct RemoteBuilder : Builder { - if (evalStore && evalStore.get() != this) { + ref store; + std::shared_ptr evalStore; + + RemoteBuilder(ref store, std::shared_ptr evalStore) + : store(store) + , evalStore(std::move(evalStore)) + { + } + +private: + + void copyDrvsFromEvalStore(const std::vector & paths); + +public: + + void buildPaths(const std::vector & drvPaths, BuildMode buildMode) override; + + std::vector + buildPathsWithResults(const std::vector & paths, BuildMode buildMode) override; + + BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) override; + + void ensurePath(const StorePath & path) override; + + /** + * The default instance would schedule the work on the client side, but + * for consistency with `buildPaths` and `buildDerivation` it should happen + * on the remote side. + * + * We make this fail for now so we can add implement this properly later + * without it being a breaking change. + */ + void repairPath(const StorePath & path) override; +}; + +void RemoteBuilder::copyDrvsFromEvalStore(const std::vector & paths) +{ + if (evalStore && evalStore.get() != &*store) { /* The remote doesn't have a way to access evalStore, so copy the .drvs. */ - RealisedPath::Set drvPaths2; + RealisedPath::Set drvPaths; for (const auto & i : paths) { std::visit( overloaded{ [&](const DerivedPath::Opaque & bp) { // Do nothing, path is hopefully there already }, - [&](const DerivedPath::Built & bp) { drvPaths2.insert(bp.drvPath->getBaseStorePath()); }, + [&](const DerivedPath::Built & bp) { drvPaths.insert(bp.drvPath->getBaseStorePath()); }, }, i.raw()); } - copyClosure(*evalStore, *this, drvPaths2); + copyClosure(*evalStore, *store, drvPaths); } } -void RemoteStore::buildPaths( - const std::vector & drvPaths, BuildMode buildMode, std::shared_ptr evalStore) +void RemoteBuilder::buildPaths(const std::vector & drvPaths, BuildMode buildMode) { - copyDrvsFromEvalStore(drvPaths, evalStore); - - auto conn(getConnection()); + copyDrvsFromEvalStore(drvPaths); + auto conn(store->getConnection()); conn->to << WorkerProto::Op::BuildPaths; - WorkerProto::write(*this, *conn, drvPaths); + WorkerProto::write(*store, *conn, drvPaths); conn->to << buildMode; conn.processStderr(); readInt(conn->from); } -std::vector RemoteStore::buildPathsWithResults( - const std::vector & paths, BuildMode buildMode, std::shared_ptr evalStore) +std::vector +RemoteBuilder::buildPathsWithResults(const std::vector & paths, BuildMode buildMode) { - copyDrvsFromEvalStore(paths, evalStore); + copyDrvsFromEvalStore(paths); - std::optional conn_(getConnection()); + std::optional conn_(store->getConnection()); auto & conn = *conn_; if (conn->protoVersion >= WorkerProto::Version{.number = {1, 34}}) { conn->to << WorkerProto::Op::BuildPathsWithResults; - WorkerProto::write(*this, *conn, paths); + WorkerProto::write(*store, *conn, paths); conn->to << buildMode; conn.processStderr(); - return WorkerProto::Serialise>::read(*this, *conn); + return WorkerProto::Serialise>::read(*store, *conn); } else { // Avoid deadlock. conn_.reset(); // Note: this throws an exception if a build/substitution // fails, but meh. - buildPaths(paths, buildMode, evalStore); + buildPaths(paths, buildMode); std::vector results; @@ -699,14 +735,14 @@ std::vector RemoteStore::buildPathsWithResults( }; OutputPathMap outputs; - auto drvPath = resolveDerivedPath(*evalStore, *bfd.drvPath); - auto built = resolveDerivedPath(*this, bfd, &*evalStore); + auto drvPath = resolveDerivedPath(*store, *bfd.drvPath); + auto built = resolveDerivedPath(*store, bfd); for (auto & [output, outputPath] : built) { auto outputId = DrvOutput{drvPath, output}; if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { - auto realisation = queryRealisation(outputId); + auto realisation = store->queryRealisation(outputId); if (!realisation) - throw MissingRealisation(*this, outputId); + throw MissingRealisation(*store, outputId); success.builtOutputs.emplace(output, *realisation); } else { success.builtOutputs.emplace( @@ -730,23 +766,34 @@ std::vector RemoteStore::buildPathsWithResults( } } -BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) +BuildResult RemoteBuilder::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) { - auto conn(getConnection()); - conn->putBuildDerivationRequest(*this, &conn.daemonException, drvPath, drv, buildMode); + auto conn(store->getConnection()); + conn->putBuildDerivationRequest(*store, &conn.daemonException, drvPath, drv, buildMode); conn.processStderr(); - return WorkerProto::Serialise::read(*this, *conn); + return WorkerProto::Serialise::read(*store, *conn); } -void RemoteStore::ensurePath(const StorePath & path) +void RemoteBuilder::ensurePath(const StorePath & path) { - auto conn(getConnection()); + auto conn(store->getConnection()); conn->to << WorkerProto::Op::EnsurePath; - WorkerProto::write(*this, *conn, path); + WorkerProto::write(*store, *conn, path); conn.processStderr(); readInt(conn->from); } +void RemoteBuilder::repairPath(const StorePath & path) +{ + throw Unsupported("operation 'repairPath' is not supported by store '%s'", store->config.getHumanReadableURI()); +} + +ref RemoteStore::getBuilder(std::shared_ptr evalStore) +{ + return make_ref( + ref(std::dynamic_pointer_cast(shared_from_this())), std::move(evalStore)); +} + void RemoteStore::addTempRoots(const StorePathSet & paths, bool skipIfSlow) { if (paths.empty()) diff --git a/src/libstore/restricted-store.cc b/src/libstore/restricted-store.cc index 6ea7752db4f6..f2f35724f412 100644 --- a/src/libstore/restricted-store.cc +++ b/src/libstore/restricted-store.cc @@ -1,8 +1,11 @@ #include "nix/store/restricted-store.hh" +#include "nix/store/build.hh" #include "nix/store/build-result.hh" +#include "nix/store/build/worker.hh" #include "nix/util/callback.hh" #include "nix/store/realisation.hh" #include "nix/store/local-store.hh" +#include "nix/util/error.hh" namespace nix { @@ -109,27 +112,11 @@ struct RestrictedStore : public virtual IndirectRootStore, public virtual GcStor void narFromPath(const StorePath & path, Sink & sink) override; - void ensurePath(const StorePath & path) override; - void registerDrvOutput(const Realisation & info) override; void queryRealisationUncached( const DrvOutput & id, Callback> callback) noexcept override; - void - buildPaths(const std::vector & paths, BuildMode buildMode, std::shared_ptr evalStore) override; - - std::vector buildPathsWithResults( - const std::vector & paths, - BuildMode buildMode = bmNormal, - std::shared_ptr evalStore = nullptr) override; - - BuildResult - buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode = bmNormal) override - { - unsupported("buildDerivation"); - } - void addTempRoots(const StorePathSet & paths, bool skipIfSlow) override {} void addIndirectRoot(const std::filesystem::path & path) override {} @@ -162,10 +149,42 @@ struct RestrictedStore : public virtual IndirectRootStore, public virtual GcStor { return NotTrusted; } + + ref getBuilder(std::shared_ptr evalStore) override + { + unreachable(); + } }; void RestrictedStore::anchor() {} +/** + * A builder that wraps an inner builder, adding restriction checks + * and dependency tracking for recursive Nix builds. + */ +struct RestrictedBuilder : Builder +{ + Worker & inner; + RestrictionContext & goal; + + RestrictedBuilder(Worker & inner, RestrictionContext & goal) + : inner(inner) + , goal(goal) + { + } + + void buildPaths(const std::vector & paths, BuildMode buildMode) override; + + std::vector + buildPathsWithResults(const std::vector & paths, BuildMode buildMode) override; + + BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) override; + + void ensurePath(const StorePath & path) override; + + void repairPath(const StorePath & path) override; +}; + ref makeRestrictedStore(ref config, ref next, RestrictionContext & context) { return make_ref(config, next, context); @@ -242,10 +261,10 @@ void RestrictedStore::narFromPath(const StorePath & path, Sink & sink) Store::narFromPath(path, sink); } -void RestrictedStore::ensurePath(const StorePath & path) +void RestrictedBuilder::ensurePath(const StorePath & path) { if (!goal.isAllowed(path)) - throw InvalidPath("cannot substitute unknown path '%s' in recursive Nix", printStorePath(path)); + throw InvalidPath("cannot substitute unknown path '%s' in recursive Nix", inner.store.printStorePath(path)); /* Nothing to be done; 'path' must already be valid. */ } @@ -266,36 +285,33 @@ void RestrictedStore::queryRealisationUncached( next->queryRealisation(id, std::move(callback)); } -void RestrictedStore::buildPaths( - const std::vector & paths, BuildMode buildMode, std::shared_ptr evalStore) +void RestrictedBuilder::buildPaths(const std::vector & paths, BuildMode buildMode) { - for (auto & result : buildPathsWithResults(paths, buildMode, evalStore)) + for (auto & result : buildPathsWithResults(paths, buildMode)) result.tryThrowBuildError(); } -std::vector RestrictedStore::buildPathsWithResults( - const std::vector & paths, BuildMode buildMode, std::shared_ptr evalStore) +std::vector +RestrictedBuilder::buildPathsWithResults(const std::vector & paths, BuildMode buildMode) { - assert(!evalStore); - if (buildMode != bmNormal) throw Error("unsupported build mode"); - StorePathSet newPaths; - std::set newRealisations; - for (auto & req : paths) { if (!goal.isAllowed(req)) - throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next)); + throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(inner.store)); } - auto results = next->buildPathsWithResults(paths, buildMode); + auto results = inner.buildPathsWithResults(paths, buildMode); + + StorePathSet newPaths; + std::set newRealisations; for (auto & result : results) { if (auto * successP = result.tryGetSuccess()) { - if (auto * pathBuilt = std::get_if(&result.path)) { + if (auto * pathBuilt = std::get_if(&result.path)) { // TODO ugly extra IO - auto drvPath = resolveDerivedPath(*next, *pathBuilt->drvPath); + auto drvPath = resolveDerivedPath(inner.store, *pathBuilt->drvPath); for (auto & [outputName, output] : successP->builtOutputs) { newPaths.insert(output.outPath); newRealisations.insert( @@ -310,7 +326,7 @@ std::vector RestrictedStore::buildPathsWithResults( } StorePathSet closure; - next->computeFSClosure(newPaths, closure); + inner.store.computeFSClosure(newPaths, closure); for (auto & path : closure) goal.addDependency(path); @@ -346,4 +362,20 @@ MissingPaths RestrictedStore::queryMissing(const std::vector & targ return res; } +ref makeRestrictedBuilder(Worker & inner, RestrictionContext & context) +{ + return make_ref(inner, context); +} + +BuildResult +RestrictedBuilder::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode) +{ + throw Unsupported("buildDerivation"); +} + +void RestrictedBuilder::repairPath(const StorePath & path) +{ + throw Unsupported("repairPath"); +} + } // namespace nix diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index b4e5fd11b9ee..f5d6a6986436 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1,3 +1,4 @@ +#include "nix/store/build/worker.hh" #include "nix/util/logging.hh" #include "nix/util/signature/local-keys.hh" #include "nix/util/source-accessor.hh" @@ -6,6 +7,7 @@ #include "nix/store/realisation.hh" #include "nix/store/derivations.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/store/store-open.hh" #include "nix/store/outputs-query.hh" #include "nix/util/util.hh" @@ -47,6 +49,8 @@ void InvalidStoreReference::anchor() {} void StoreConfigBase::anchor() {} +void Builder::anchor() {} + static std::string canonStoreDir(std::string path) { if (path.empty() || path[0] != '/') @@ -143,6 +147,13 @@ std::pair StoreDirConfig::toStorePath(std::string_view pat return {parseStorePath(path.substr(0, slash)), CanonPath{path.substr(slash)}}; } +ref Store::getBuilder(std::shared_ptr evalStore) +{ + auto store = ref(shared_from_this()); + auto evalStoreRef = evalStore ? ref(std::move(evalStore)) : store; + return make_ref(store, evalStoreRef); +} + std::filesystem::path Store::followLinksToStore(std::string_view _path) const { auto path = absPath(std::string(_path)); @@ -777,7 +788,7 @@ void Store::substitutePaths(const StorePathSet & paths) std::vector subs; for (auto & p : missing.willSubstitute) subs.emplace_back(DerivedPath::Opaque{p}); - buildPaths(subs); + getBuilder()->buildPaths(subs, bmNormal); } catch (Error & e) { logWarning(e.info()); } @@ -1236,7 +1247,7 @@ decodeValidPathInfo(const Store & store, std::istream & str, std::optionalensurePath(drvPath); return readDerivation(drvPath); } diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc index 3836fb7b5937..5cb3a56e3531 100644 --- a/src/libstore/unix/build/derivation-builder.cc +++ b/src/libstore/unix/build/derivation-builder.cc @@ -9,7 +9,6 @@ #include "nix/util/util.hh" #include "nix/util/archive.hh" #include "nix/util/git.hh" -#include "nix/store/daemon.hh" #include "nix/util/topo-sort.hh" #include "nix/store/build/child.hh" #include "nix/util/unix-domain-socket.hh" @@ -887,10 +886,9 @@ void DerivationBuilderImpl::startDaemon() auto doneFlag = make_ref(); - auto workerThread = std::thread([doneFlag, store, remote{std::move(remote)}]() { + auto workerThread = std::thread([this, doneFlag, store, remote{std::move(remote)}]() { try { - daemon::processConnection( - store, FdSource(remote.get()), FdSink(remote.get()), NotTrusted, daemon::Recursive); + miscMethods->processDaemonConnection(store, FdSource(remote.get()), FdSink(remote.get()), *this); debug("terminated daemon connection"); } catch (const Interrupted &) { debug("interrupted daemon connection"); diff --git a/src/nix/build-remote/build-remote.cc b/src/nix/build-remote/build-remote.cc index 8f8f21a8c39c..648f05a489c1 100644 --- a/src/nix/build-remote/build-remote.cc +++ b/src/nix/build-remote/build-remote.cc @@ -21,6 +21,7 @@ #include "nix/util/strings.hh" #include "nix/store/derivations.hh" #include "nix/store/local-store.hh" +#include "nix/store/build.hh" #include "nix/cmd/legacy.hh" #include "nix/util/experimental-features.hh" #include "nix/store/globals.hh" @@ -338,7 +339,7 @@ static int main_build_remote(int argc, char ** argv) // output ids, which break CA derivations if (!drv.inputDrvs.map.empty()) drv.inputSrcs = store->parseStorePathSet(inputs); - optResult = sshStore->buildDerivation(*drvPath, static_cast(drv)); + optResult = sshStore->getBuilder()->buildDerivation(*drvPath, static_cast(drv)); auto & result = *optResult; if (auto * failureP = result.tryGetFailure()) { if (settings.keepFailed) { @@ -353,7 +354,7 @@ static int main_build_remote(int argc, char ** argv) } } else { copyClosure(*store, *sshStore, StorePathSet{*drvPath}, NoRepair, NoCheckSigs, substitute); - auto res = sshStore->buildPathsWithResults({DerivedPath::Built{ + auto res = sshStore->getBuilder()->buildPathsWithResults({DerivedPath::Built{ .drvPath = makeConstantStorePathRef(*drvPath), .outputs = OutputsSpec::All{}, }}); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 659fc8315d17..2e6d55526882 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -2,6 +2,7 @@ #include "nix/cmd/command-installable-value.hh" #include "nix/main/shared.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/store/local-fs-store.hh" #include "nix/expr/eval-inline.hh" #include "nix/store/globals.hh" @@ -103,7 +104,7 @@ struct CmdBundle : InstallableValueCommand evalState->waitForAllPaths(); - store->buildPaths({ + store->getBuilder()->buildPaths({ DerivedPath::Built{ .drvPath = makeConstantStorePathRef(drvPath), .outputs = OutputsSpec::All{}, diff --git a/src/nix/develop.cc b/src/nix/develop.cc index ec190d26f6fe..bd0715c40eaa 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -6,6 +6,7 @@ #include "nix/main/common-args.hh" #include "nix/main/shared.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/store/globals.hh" #include "nix/store/outputs-spec.hh" #include "nix/store/outputs-query.hh" @@ -291,13 +292,12 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore auto shellDrvPath = evalStore->writeDerivation(drv); /* Build the derivation. */ - store->buildPaths( + store->getBuilder(evalStore)->buildPaths( {DerivedPath::Built{ .drvPath = makeConstantStorePathRef(shellDrvPath), .outputs = OutputsSpec::All{}, }}, - bmNormal, - evalStore); + bmNormal); // `get-env.sh` will write its JSON output to an arbitrary output // path, so return the first non-empty output path. diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3deddb7dc6e6..1e7a1dc9af9d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -21,6 +21,7 @@ #include "nix/util/users.hh" #include "nix/fetchers/fetch-to-store.hh" #include "nix/store/local-fs-store.hh" +#include "nix/store/build.hh" #include "nix/store/globals.hh" #include "nix/expr/parallel-eval.hh" #include "nix/util/exit.hh" @@ -530,7 +531,7 @@ struct CmdFlakeCheck : FlakeCommand, MixPrintOutPaths, MixOutLinkBase, MixFlakeS // FIXME: should start building while evaluating. Activity act(*logger, lvlInfo, actUnknown, fmt("running %d flake checks", toBuild.size())); - auto buildResults = store->buildPathsWithResults(toBuild); + auto buildResults = store->getBuilder()->buildPathsWithResults(toBuild); assert(buildResults.size() == toBuild.size()); for (auto & buildResult : buildResults) { diff --git a/src/nix/nix-build/nix-build.cc b/src/nix/nix-build/nix-build.cc index d2e927a2d2ee..03d2fd7318ec 100644 --- a/src/nix/nix-build/nix-build.cc +++ b/src/nix/nix-build/nix-build.cc @@ -28,6 +28,7 @@ #include "nix/util/users.hh" #include "nix/cmd/network-proxy.hh" #include "nix/cmd/compatibility-settings.hh" +#include "nix/store/build.hh" #include "nix/util/fun.hh" #include "man-pages.hh" @@ -451,7 +452,7 @@ static void main_nix_build(int argc, char ** argv) printMissing(ref(store), paths); if (!dryRun) - store->buildPaths(paths, buildMode, evalStore); + store->getBuilder(evalStore)->buildPaths(paths, buildMode); }; if (isNixShell) { diff --git a/src/nix/nix-env/nix-env.cc b/src/nix/nix-env/nix-env.cc index 4a2238ed1925..ff3ee7c31b2e 100644 --- a/src/nix/nix-env/nix-env.cc +++ b/src/nix/nix-env/nix-env.cc @@ -12,6 +12,7 @@ #include "nix/store/path-with-outputs.hh" #include "nix/main/shared.hh" #include "nix/store/store-open.hh" +#include "nix/store/build.hh" #include "nix/store/local-fs-store.hh" #include "user-env.hh" #include "nix/expr/value-to-json.hh" @@ -794,7 +795,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) printMissing(globals.state->store, paths); if (globals.dryRun) return; - globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal); + globals.state->store->getBuilder()->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal); debug("switching to new user environment"); auto generation = createGeneration(*store2, globals.profile, drv.queryOutPath()); diff --git a/src/nix/nix-env/user-env.cc b/src/nix/nix-env/user-env.cc index 5cd3bba9f430..0ae97e1689af 100644 --- a/src/nix/nix-env/user-env.cc +++ b/src/nix/nix-env/user-env.cc @@ -1,6 +1,7 @@ #include "user-env.hh" #include "nix/store/derivations.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" #include "nix/store/path-with-outputs.hh" #include "nix/store/local-fs-store.hh" #include "nix/main/shared.hh" @@ -46,7 +47,7 @@ bool createUserEnv( } debug("building user environment dependencies"); - state.store->buildPaths(toDerivedPaths(drvsToBuild), state.repair ? bmRepair : bmNormal); + state.store->getBuilder()->buildPaths(toDerivedPaths(drvsToBuild), state.repair ? bmRepair : bmNormal); /* Construct the whole top level derivation. */ StorePathSet references; @@ -81,7 +82,7 @@ bool createUserEnv( /* This is only necessary when installing store paths, e.g., `nix-env -i /nix/store/abcd...-foo'. */ state.store->addTempRoot(*j.second); - state.store->ensurePath(*j.second); + state.store->getBuilder()->ensurePath(*j.second); references.insert(*j.second); } @@ -157,7 +158,7 @@ bool createUserEnv( std::vector topLevelDrvs; topLevelDrvs.push_back({topLevelDrv}); state.waitForPath(topLevelDrv); - state.store->buildPaths(toDerivedPaths(topLevelDrvs), state.repair ? bmRepair : bmNormal); + state.store->getBuilder()->buildPaths(toDerivedPaths(topLevelDrvs), state.repair ? bmRepair : bmNormal); /* Switch the current user environment to the output path. */ auto store2 = state.store.dynamic_pointer_cast(); diff --git a/src/nix/nix-store/nix-store.cc b/src/nix/nix-store/nix-store.cc index 59928479aba6..fc1fc576b9b7 100644 --- a/src/nix/nix-store/nix-store.cc +++ b/src/nix/nix-store/nix-store.cc @@ -21,6 +21,7 @@ #include "nix/store/posix-fs-canonicalise.hh" #include "nix/util/error.hh" #include "nix/store/gc-store.hh" +#include "nix/store/build.hh" #include "man-pages.hh" @@ -76,7 +77,7 @@ static std::set realisePath(StorePathWithOutputs path, bo if (path.path.isDerivation()) { if (build) - store->buildPaths({path.toDerivedPath()}); + store->getBuilder()->buildPaths({path.toDerivedPath()}); auto outputPaths = deepQueryDerivationOutputMap(*store, path.path); Derivation drv = store->derivationFromPath(path.path); rootNr++; @@ -113,7 +114,7 @@ static std::set realisePath(StorePathWithOutputs path, bo else { if (build) - store->ensurePath(path.path); + store->getBuilder()->ensurePath(path.path); else if (!store->isValidPath(path.path)) throw Error("path '%s' does not exist and cannot be created", store->printStorePath(path.path)); if (store2) { @@ -173,7 +174,7 @@ static void opRealise(Strings opFlags, Strings opArgs) return; /* Build all paths at the same time to exploit parallelism. */ - store->buildPaths(toDerivedPaths(paths), buildMode); + store->getBuilder()->buildPaths(toDerivedPaths(paths), buildMode); if (!ignoreUnknown) for (auto & i : paths) { @@ -862,7 +863,7 @@ static void opRepairPath(Strings opFlags, Strings opArgs) throw UsageError("no flags expected"); for (auto & i : opArgs) - store->repairPath(store->followLinksToStorePath(i)); + store->getBuilder()->repairPath(store->followLinksToStorePath(i)); } /* Optimise the disk space usage of the Nix store by hard-linking @@ -1008,7 +1009,7 @@ static void opServe(Strings opFlags, Strings opArgs) #ifndef _WIN32 // TODO figure out if Windows needs something similar MonitorFdHup monitor(in.fd); #endif - store->buildPaths(toDerivedPaths(paths)); + store->getBuilder()->buildPaths(toDerivedPaths(paths)); out << 0; } catch (Error & e) { assert(e.info().status); @@ -1031,7 +1032,7 @@ static void opServe(Strings opFlags, Strings opArgs) #ifndef _WIN32 // TODO figure out if Windows needs something similar MonitorFdHup monitor(in.fd); #endif - auto status = store->buildDerivation(drvPath, drv); + auto status = store->getBuilder()->buildDerivation(drvPath, drv); ServeProto::write(*store, wconn, status); break; diff --git a/src/nix/provenance.cc b/src/nix/provenance.cc index ff7086944049..baf9f2974e84 100644 --- a/src/nix/provenance.cc +++ b/src/nix/provenance.cc @@ -1,4 +1,5 @@ #include "nix/cmd/command.hh" +#include "nix/store/build.hh" #include "nix/store/store-api.hh" #include "nix/store/store-open.hh" #include "nix/expr/provenance.hh" @@ -380,7 +381,7 @@ struct CmdProvenanceVerify : StorePathsCommand "⏭️ skipped rebuild of derivation '%s^%s'", store.printStorePath(build->drvPath), build->output); } else { try { - store.buildPaths( + store.getBuilder()->buildPaths( {DerivedPath::Built{ .drvPath = make_ref(SingleDerivedPath::Opaque{build->drvPath}), .outputs = OutputsSpec::Names{build->output}, diff --git a/src/nix/store-repair.cc b/src/nix/store-repair.cc index a6eee6dfbf2c..6c91d1959ac1 100644 --- a/src/nix/store-repair.cc +++ b/src/nix/store-repair.cc @@ -1,5 +1,6 @@ #include "nix/cmd/command.hh" #include "nix/store/store-api.hh" +#include "nix/store/build.hh" namespace nix { @@ -20,7 +21,7 @@ struct CmdStoreRepair : StorePathsCommand void run(ref store, StorePaths && storePaths) override { for (auto & path : storePaths) - store->repairPath(path); + store->getBuilder()->repairPath(path); } }; diff --git a/tests/functional/test-libstoreconsumer/main.cc b/tests/functional/test-libstoreconsumer/main.cc index cab02d799d79..9ba21306ab5e 100644 --- a/tests/functional/test-libstoreconsumer/main.cc +++ b/tests/functional/test-libstoreconsumer/main.cc @@ -1,5 +1,6 @@ #include "nix/store/globals.hh" #include "nix/store/store-open.hh" +#include "nix/store/build.hh" #include "nix/store/build-result.hh" #include @@ -24,7 +25,7 @@ int main(int argc, char ** argv) std::vector paths{DerivedPath::Built{ .drvPath = makeConstantStorePathRef(store->parseStorePath(drvPath)), .outputs = OutputsSpec::Names{"out"}}}; - const auto results = store->buildPathsWithResults(paths, bmNormal, store); + const auto results = store->getBuilder()->buildPathsWithResults(paths, bmNormal); for (const auto & result : results) { if (auto * successP = result.tryGetSuccess()) {