buildPaths(): Add an evalStore argument

With this, we don't have to copy the entire .drv closure to the
destination store ahead of time (or at all). Instead, buildPaths()
reads .drv files from the eval store and copies inputSrcs to the
destination store if it needs to build a derivation.

Issue #5025.
This commit is contained in:
Eelco Dolstra 2021-07-19 15:43:08 +02:00
parent 668abd3e57
commit eb6db4fd38
13 changed files with 55 additions and 38 deletions

View file

@ -766,7 +766,7 @@ BuiltPaths build(ref<Store> evalStore, ref<Store> store, Realise mode,
if (mode == Realise::Nothing) if (mode == Realise::Nothing)
printMissing(store, pathsToBuild, lvlError); printMissing(store, pathsToBuild, lvlError);
else if (mode == Realise::Outputs) else if (mode == Realise::Outputs)
store->buildPaths(pathsToBuild, bMode); store->buildPaths(pathsToBuild, bMode, evalStore);
return getBuiltPaths(evalStore, store, pathsToBuild); return getBuiltPaths(evalStore, store, pathsToBuild);
} }

View file

@ -165,7 +165,7 @@ void DerivationGoal::getDerivation()
/* The first thing to do is to make sure that the derivation /* The first thing to do is to make sure that the derivation
exists. If it doesn't, it may be created through a exists. If it doesn't, it may be created through a
substitute. */ substitute. */
if (buildMode == bmNormal && worker.store.isValidPath(drvPath)) { if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) {
loadDerivation(); loadDerivation();
return; return;
} }
@ -188,12 +188,12 @@ void DerivationGoal::loadDerivation()
/* `drvPath' should already be a root, but let's be on the safe /* `drvPath' should already be a root, but let's be on the safe
side: if the user forgot to make it a root, we wouldn't want side: if the user forgot to make it a root, we wouldn't want
things being garbage collected while we're busy. */ things being garbage collected while we're busy. */
worker.store.addTempRoot(drvPath); worker.evalStore.addTempRoot(drvPath);
assert(worker.store.isValidPath(drvPath)); assert(worker.evalStore.isValidPath(drvPath));
/* Get the derivation. */ /* Get the derivation. */
drv = std::make_unique<Derivation>(worker.store.derivationFromPath(drvPath)); drv = std::make_unique<Derivation>(worker.evalStore.derivationFromPath(drvPath));
haveDerivation(); haveDerivation();
} }
@ -212,8 +212,8 @@ void DerivationGoal::haveDerivation()
if (i.second.second) if (i.second.second)
worker.store.addTempRoot(*i.second.second); worker.store.addTempRoot(*i.second.second);
auto outputHashes = staticOutputHashes(worker.store, *drv); auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
for (auto &[outputName, outputHash] : outputHashes) for (auto & [outputName, outputHash] : outputHashes)
initialOutputs.insert({ initialOutputs.insert({
outputName, outputName,
InitialOutput{ InitialOutput{
@ -337,6 +337,16 @@ void DerivationGoal::gaveUpOnSubstitution()
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs)
addWaitee(worker.makeDerivationGoal(i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal)); addWaitee(worker.makeDerivationGoal(i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal));
/* Copy the input sources from the eval store to the build
store. */
if (&worker.evalStore != &worker.store) {
RealisedPath::Set inputSrcs, inputClosure;
for (auto & i : drv->inputSrcs)
inputSrcs.insert(i);
RealisedPath::closure(worker.evalStore, inputSrcs, inputClosure);
copyClosure(worker.evalStore, worker.store, inputClosure);
}
for (auto & i : drv->inputSrcs) { for (auto & i : drv->inputSrcs) {
if (worker.store.isValidPath(i)) continue; if (worker.store.isValidPath(i)) continue;
if (!settings.useSubstitutes) if (!settings.useSubstitutes)
@ -478,8 +488,8 @@ void DerivationGoal::inputsRealised()
/* Add the relevant output closures of the input derivation /* Add the relevant output closures of the input derivation
`i' as input paths. Only add the closures of output paths `i' as input paths. Only add the closures of output paths
that are specified as inputs. */ that are specified as inputs. */
assert(worker.store.isValidPath(drvPath)); assert(worker.evalStore.isValidPath(drvPath));
auto outputs = worker.store.queryPartialDerivationOutputMap(depDrvPath); auto outputs = worker.evalStore.queryPartialDerivationOutputMap(depDrvPath);
for (auto & j : wantedDepOutputs) { for (auto & j : wantedDepOutputs) {
if (outputs.count(j) > 0) { if (outputs.count(j) > 0) {
auto optRealizedInput = outputs.at(j); auto optRealizedInput = outputs.at(j);

View file

@ -6,9 +6,9 @@
namespace nix { namespace nix {
void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode) void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode, std::shared_ptr<Store> evalStore)
{ {
Worker worker(*this); Worker worker(*this, evalStore ? *evalStore : *this);
Goals goals; Goals goals;
for (auto & br : reqs) { for (auto & br : reqs) {
@ -51,7 +51,7 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
BuildMode buildMode) BuildMode buildMode)
{ {
Worker worker(*this); Worker worker(*this, *this);
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode); auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode);
BuildResult result; BuildResult result;
@ -93,7 +93,7 @@ void Store::ensurePath(const StorePath & path)
/* If the path is already valid, we're done. */ /* If the path is already valid, we're done. */
if (isValidPath(path)) return; if (isValidPath(path)) return;
Worker worker(*this); Worker worker(*this, *this);
GoalPtr goal = worker.makePathSubstitutionGoal(path); GoalPtr goal = worker.makePathSubstitutionGoal(path);
Goals goals = {goal}; Goals goals = {goal};
@ -111,7 +111,7 @@ void Store::ensurePath(const StorePath & path)
void LocalStore::repairPath(const StorePath & path) void LocalStore::repairPath(const StorePath & path)
{ {
Worker worker(*this); Worker worker(*this, *this);
GoalPtr goal = worker.makePathSubstitutionGoal(path, Repair); GoalPtr goal = worker.makePathSubstitutionGoal(path, Repair);
Goals goals = {goal}; Goals goals = {goal};

View file

@ -1254,8 +1254,10 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
return next->queryRealisation(id); return next->queryRealisation(id);
} }
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode) override void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
{ {
assert(!evalStore);
if (buildMode != bmNormal) throw Error("unsupported build mode"); if (buildMode != bmNormal) throw Error("unsupported build mode");
StorePathSet newPaths; StorePathSet newPaths;

View file

@ -9,11 +9,12 @@
namespace nix { namespace nix {
Worker::Worker(Store & store) Worker::Worker(Store & store, Store & evalStore)
: act(*logger, actRealise) : act(*logger, actRealise)
, actDerivations(*logger, actBuilds) , actDerivations(*logger, actBuilds)
, actSubstitutions(*logger, actCopyPaths) , actSubstitutions(*logger, actCopyPaths)
, store(store) , store(store)
, evalStore(evalStore)
{ {
/* Debugging: prevent recursive workers. */ /* Debugging: prevent recursive workers. */
nrLocalBuilds = 0; nrLocalBuilds = 0;

View file

@ -110,6 +110,7 @@ public:
bool checkMismatch; bool checkMismatch;
Store & store; Store & store;
Store & evalStore;
std::unique_ptr<HookInstance> hook; std::unique_ptr<HookInstance> hook;
@ -131,7 +132,7 @@ public:
it answers with "decline-permanently", we don't try again. */ it answers with "decline-permanently", we don't try again. */
bool tryBuildHook = true; bool tryBuildHook = true;
Worker(Store & store); Worker(Store & store, Store & evalStore);
~Worker(); ~Worker();
/* Make a goal (with caching). */ /* Make a goal (with caching). */

View file

@ -267,8 +267,11 @@ public:
return status; return status;
} }
void buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode) override void buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
{ {
if (evalStore && evalStore.get() != this)
throw Error("building on an SSH store is incompatible with '--eval-store'");
auto conn(connections->get()); auto conn(connections->get());
conn->to << cmdBuildPaths; conn->to << cmdBuildPaths;

View file

@ -45,7 +45,7 @@ struct Realisation {
size_t checkSignatures(const PublicKeys & publicKeys) const; size_t checkSignatures(const PublicKeys & publicKeys) const;
static std::set<Realisation> closure(Store &, const std::set<Realisation> &); static std::set<Realisation> closure(Store &, const std::set<Realisation> &);
static void closure(Store &, const std::set<Realisation> &, std::set<Realisation>& res); static void closure(Store &, const std::set<Realisation> &, std::set<Realisation> & res);
bool isCompatibleWith(const Realisation & other) const; bool isCompatibleWith(const Realisation & other) const;

View file

@ -705,8 +705,11 @@ static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, cons
} }
} }
void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode) void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
{ {
if (evalStore && evalStore.get() != this)
throw Error("building on a remote store is incompatible with '--eval-store'");
auto conn(getConnection()); auto conn(getConnection());
conn->to << wopBuildPaths; conn->to << wopBuildPaths;
assert(GET_PROTOCOL_MINOR(conn->daemonVersion) >= 13); assert(GET_PROTOCOL_MINOR(conn->daemonVersion) >= 13);

View file

@ -85,7 +85,7 @@ public:
std::optional<const Realisation> queryRealisation(const DrvOutput &) override; std::optional<const Realisation> queryRealisation(const DrvOutput &) override;
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode) override; void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
BuildMode buildMode) override; BuildMode buildMode) override;

View file

@ -497,7 +497,8 @@ public:
not derivations, substitute them. */ not derivations, substitute them. */
virtual void buildPaths( virtual void buildPaths(
const std::vector<DerivedPath> & paths, const std::vector<DerivedPath> & paths,
BuildMode buildMode = bmNormal); BuildMode buildMode = bmNormal,
std::shared_ptr<Store> evalStore = nullptr);
/* Build a single non-materialized derivation (i.e. not from an /* Build a single non-materialized derivation (i.e. not from an
on-disk .drv file). on-disk .drv file).

View file

@ -341,7 +341,7 @@ static void main_nix_build(int argc, char * * argv)
printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize); printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize);
if (!dryRun) if (!dryRun)
store->buildPaths(paths, buildMode); store->buildPaths(paths, buildMode, evalStore);
}; };
if (runEnv) { if (runEnv) {
@ -397,8 +397,6 @@ static void main_nix_build(int argc, char * * argv)
pathsToCopy.insert(src); pathsToCopy.insert(src);
} }
copyClosure(*evalStore, *store, pathsToCopy);
buildPaths(pathsToBuild); buildPaths(pathsToBuild);
if (dryRun) return; if (dryRun) return;
@ -449,7 +447,7 @@ static void main_nix_build(int argc, char * * argv)
if (env.count("__json")) { if (env.count("__json")) {
StorePathSet inputs; StorePathSet inputs;
for (auto & [depDrvPath, wantedDepOutputs] : drv.inputDrvs) { for (auto & [depDrvPath, wantedDepOutputs] : drv.inputDrvs) {
auto outputs = store->queryPartialDerivationOutputMap(depDrvPath); auto outputs = evalStore->queryPartialDerivationOutputMap(depDrvPath);
for (auto & i : wantedDepOutputs) { for (auto & i : wantedDepOutputs) {
auto o = outputs.at(i); auto o = outputs.at(i);
store->computeFSClosure(*o, inputs); store->computeFSClosure(*o, inputs);
@ -564,8 +562,6 @@ static void main_nix_build(int argc, char * * argv)
drvMap[drvPath] = {drvMap.size(), {outputName}}; drvMap[drvPath] = {drvMap.size(), {outputName}};
} }
copyClosure(*evalStore, *store, drvsToCopy);
buildPaths(pathsToBuild); buildPaths(pathsToBuild);
if (dryRun) return; if (dryRun) return;
@ -578,7 +574,7 @@ static void main_nix_build(int argc, char * * argv)
if (counter) if (counter)
drvPrefix += fmt("-%d", counter + 1); drvPrefix += fmt("-%d", counter + 1);
auto builtOutputs = store->queryPartialDerivationOutputMap(drvPath); auto builtOutputs = evalStore->queryPartialDerivationOutputMap(drvPath);
auto maybeOutputPath = builtOutputs.at(outputName); auto maybeOutputPath = builtOutputs.at(outputName);
assert(maybeOutputPath); assert(maybeOutputPath);

View file

@ -171,15 +171,15 @@ const static std::string getEnvSh =
modified derivation with the same dependencies and nearly the same modified derivation with the same dependencies and nearly the same
initial environment variables, that just writes the resulting initial environment variables, that just writes the resulting
environment to a file and exits. */ environment to a file and exits. */
StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath) static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore, const StorePath & drvPath)
{ {
auto drv = store->derivationFromPath(drvPath); auto drv = evalStore->derivationFromPath(drvPath);
auto builder = baseNameOf(drv.builder); auto builder = baseNameOf(drv.builder);
if (builder != "bash") if (builder != "bash")
throw Error("'nix develop' only works on derivations that use 'bash' as their builder"); throw Error("'nix develop' only works on derivations that use 'bash' as their builder");
auto getEnvShPath = store->addTextToStore("get-env.sh", getEnvSh, {}); auto getEnvShPath = evalStore->addTextToStore("get-env.sh", getEnvSh, {});
drv.args = {store->printStorePath(getEnvShPath)}; drv.args = {store->printStorePath(getEnvShPath)};
@ -205,7 +205,7 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } }; output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } };
drv.env[output.first] = ""; drv.env[output.first] = "";
} }
Hash h = std::get<0>(hashDerivationModulo(*store, drv, true)); Hash h = std::get<0>(hashDerivationModulo(*evalStore, drv, true));
for (auto & output : drv.outputs) { for (auto & output : drv.outputs) {
auto outPath = store->makeOutputPath(output.first, h, drv.name); auto outPath = store->makeOutputPath(output.first, h, drv.name);
@ -214,12 +214,12 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
} }
} }
auto shellDrvPath = writeDerivation(*store, drv); auto shellDrvPath = writeDerivation(*evalStore, drv);
/* Build the derivation. */ /* Build the derivation. */
store->buildPaths({DerivedPath::Built{shellDrvPath}}); store->buildPaths({DerivedPath::Built{shellDrvPath}}, bmNormal, evalStore);
for (auto & [_0, optPath] : store->queryPartialDerivationOutputMap(shellDrvPath)) { for (auto & [_0, optPath] : evalStore->queryPartialDerivationOutputMap(shellDrvPath)) {
assert(optPath); assert(optPath);
auto & outPath = *optPath; auto & outPath = *optPath;
assert(store->isValidPath(outPath)); assert(store->isValidPath(outPath));
@ -347,7 +347,7 @@ struct Common : InstallableCommand, MixProfile
auto & drvPath = *drvs.begin(); auto & drvPath = *drvs.begin();
return getDerivationEnvironment(store, drvPath); return getDerivationEnvironment(store, getEvalStore(), drvPath);
} }
} }
@ -361,7 +361,7 @@ struct Common : InstallableCommand, MixProfile
debug("reading environment file '%s'", strPath); debug("reading environment file '%s'", strPath);
return {BuildEnvironment::fromJSON(readFile(strPath)), strPath}; return {BuildEnvironment::fromJSON(readFile(store->toRealPath(shellOutPath))), strPath};
} }
}; };