mirror of
https://github.com/NixOS/nix.git
synced 2025-12-16 14:01:05 +01:00
Add paths to the store asynchronously
Adding paths to the store can be slow due to I/O overhead, but especially when going through the daemon because of the round-trip latency of every wopAddToStore call. So we now do the addToStore() calls asynchronously from a separate thread from the evaluator. This slightly speeds up the local store, and makes going through the daemon almost as fast as a local store.
This commit is contained in:
parent
bb600e1048
commit
852a2bae91
21 changed files with 267 additions and 4 deletions
|
|
@ -89,7 +89,8 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
DerivedPathsWithInfo res;
|
DerivedPathsWithInfo res;
|
||||||
for (auto & [drvPath, outputs] : byDrvPath)
|
for (auto & [drvPath, outputs] : byDrvPath) {
|
||||||
|
state->waitForPath(drvPath);
|
||||||
res.push_back({
|
res.push_back({
|
||||||
.path =
|
.path =
|
||||||
DerivedPath::Built{
|
DerivedPath::Built{
|
||||||
|
|
@ -102,6 +103,7 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
|
||||||
so we can fill in this info. */
|
so we can fill in this info. */
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
auto drvPath = attr->forceDerivation();
|
auto drvPath = attr->forceDerivation();
|
||||||
|
state->waitForPath(drvPath);
|
||||||
|
|
||||||
std::optional<NixInt::Inner> priority;
|
std::optional<NixInt::Inner> priority;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -333,6 +333,7 @@ StorePath NixRepl::getDerivationPath(Value & v)
|
||||||
auto drvPath = packageInfo->queryDrvPath();
|
auto drvPath = packageInfo->queryDrvPath();
|
||||||
if (!drvPath)
|
if (!drvPath)
|
||||||
throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)");
|
throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)");
|
||||||
|
state->waitForPath(*drvPath);
|
||||||
if (!state->store->isValidPath(*drvPath))
|
if (!state->store->isValidPath(*drvPath))
|
||||||
throw Error("expression evaluated to invalid derivation '%s'", state->store->printStorePath(*drvPath));
|
throw Error("expression evaluated to invalid derivation '%s'", state->store->printStorePath(*drvPath));
|
||||||
return *drvPath;
|
return *drvPath;
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ nix_err nix_expr_eval_from_string(
|
||||||
nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path)));
|
nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path)));
|
||||||
state->state.eval(parsedExpr, value->value);
|
state->state.eval(parsedExpr, value->value);
|
||||||
state->state.forceValue(value->value, nix::noPos);
|
state->state.forceValue(value->value, nix::noPos);
|
||||||
|
state->state.waitForAllPaths();
|
||||||
}
|
}
|
||||||
NIXC_CATCH_ERRS
|
NIXC_CATCH_ERRS
|
||||||
}
|
}
|
||||||
|
|
@ -80,6 +81,7 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, n
|
||||||
try {
|
try {
|
||||||
state->state.callFunction(fn->value, arg->value, value->value, nix::noPos);
|
state->state.callFunction(fn->value, arg->value, value->value, nix::noPos);
|
||||||
state->state.forceValue(value->value, nix::noPos);
|
state->state.forceValue(value->value, nix::noPos);
|
||||||
|
state->state.waitForAllPaths();
|
||||||
}
|
}
|
||||||
NIXC_CATCH_ERRS
|
NIXC_CATCH_ERRS
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +94,7 @@ nix_err nix_value_call_multi(
|
||||||
try {
|
try {
|
||||||
state->state.callFunction(fn->value, {(nix::Value **) args, nargs}, value->value, nix::noPos);
|
state->state.callFunction(fn->value, {(nix::Value **) args, nargs}, value->value, nix::noPos);
|
||||||
state->state.forceValue(value->value, nix::noPos);
|
state->state.forceValue(value->value, nix::noPos);
|
||||||
|
state->state.waitForAllPaths();
|
||||||
}
|
}
|
||||||
NIXC_CATCH_ERRS
|
NIXC_CATCH_ERRS
|
||||||
}
|
}
|
||||||
|
|
@ -102,6 +105,7 @@ nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value *
|
||||||
context->last_err_code = NIX_OK;
|
context->last_err_code = NIX_OK;
|
||||||
try {
|
try {
|
||||||
state->state.forceValue(value->value, nix::noPos);
|
state->state.forceValue(value->value, nix::noPos);
|
||||||
|
state->state.waitForAllPaths();
|
||||||
}
|
}
|
||||||
NIXC_CATCH_ERRS
|
NIXC_CATCH_ERRS
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +116,7 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_val
|
||||||
context->last_err_code = NIX_OK;
|
context->last_err_code = NIX_OK;
|
||||||
try {
|
try {
|
||||||
state->state.forceValueDeep(value->value);
|
state->state.forceValueDeep(value->value);
|
||||||
|
state->state.waitForAllPaths();
|
||||||
}
|
}
|
||||||
NIXC_CATCH_ERRS
|
NIXC_CATCH_ERRS
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,7 @@ nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value
|
||||||
if (attr) {
|
if (attr) {
|
||||||
nix_gc_incref(nullptr, attr->value);
|
nix_gc_incref(nullptr, attr->value);
|
||||||
state->state.forceValue(*attr->value, nix::noPos);
|
state->state.forceValue(*attr->value, nix::noPos);
|
||||||
|
state->state.waitForAllPaths();
|
||||||
return as_nix_value_ptr(attr->value);
|
return as_nix_value_ptr(attr->value);
|
||||||
}
|
}
|
||||||
nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute");
|
nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute");
|
||||||
|
|
|
||||||
|
|
@ -708,6 +708,7 @@ StorePath AttrCursor::forceDerivation()
|
||||||
/* The eval cache contains 'drvPath', but the actual path has
|
/* The eval cache contains 'drvPath', but the actual path has
|
||||||
been garbage-collected. So force it to be regenerated. */
|
been garbage-collected. So force it to be regenerated. */
|
||||||
aDrvPath->forceValue();
|
aDrvPath->forceValue();
|
||||||
|
root->state.waitForPath(drvPath);
|
||||||
if (!root->state.store->isValidPath(drvPath))
|
if (!root->state.store->isValidPath(drvPath))
|
||||||
throw Error(
|
throw Error(
|
||||||
"don't know how to recreate store derivation '%s'!", root->state.store->printStorePath(drvPath));
|
"don't know how to recreate store derivation '%s'!", root->state.store->printStorePath(drvPath));
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include "nix/fetchers/fetch-to-store.hh"
|
#include "nix/fetchers/fetch-to-store.hh"
|
||||||
#include "nix/fetchers/tarball.hh"
|
#include "nix/fetchers/tarball.hh"
|
||||||
#include "nix/fetchers/input-cache.hh"
|
#include "nix/fetchers/input-cache.hh"
|
||||||
|
#include "nix/store/async-path-writer.hh"
|
||||||
|
|
||||||
#include "parser-tab.hh"
|
#include "parser-tab.hh"
|
||||||
|
|
||||||
|
|
@ -326,6 +327,7 @@ EvalState::EvalState(
|
||||||
, debugRepl(nullptr)
|
, debugRepl(nullptr)
|
||||||
, debugStop(false)
|
, debugStop(false)
|
||||||
, trylevel(0)
|
, trylevel(0)
|
||||||
|
, asyncPathWriter(AsyncPathWriter::make(store))
|
||||||
, regexCache(makeRegexCache())
|
, regexCache(makeRegexCache())
|
||||||
#if NIX_USE_BOEHMGC
|
#if NIX_USE_BOEHMGC
|
||||||
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||||
|
|
@ -1024,6 +1026,7 @@ std::string EvalState::mkSingleDerivedPathStringRaw(const SingleDerivedPath & p)
|
||||||
auto optStaticOutputPath = std::visit(
|
auto optStaticOutputPath = std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
[&](const SingleDerivedPath::Opaque & o) {
|
[&](const SingleDerivedPath::Opaque & o) {
|
||||||
|
waitForPath(o.path);
|
||||||
auto drv = store->readDerivation(o.path);
|
auto drv = store->readDerivation(o.path);
|
||||||
auto i = drv.outputs.find(b.output);
|
auto i = drv.outputs.find(b.output);
|
||||||
if (i == drv.outputs.end())
|
if (i == drv.outputs.end())
|
||||||
|
|
@ -3249,4 +3252,24 @@ void forceNoNullByte(std::string_view s, std::function<Pos()> pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EvalState::waitForPath(const StorePath & path)
|
||||||
|
{
|
||||||
|
asyncPathWriter->waitForPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvalState::waitForPath(const SingleDerivedPath & path)
|
||||||
|
{
|
||||||
|
std::visit(
|
||||||
|
overloaded{
|
||||||
|
[&](const DerivedPathOpaque & p) { waitForPath(p.path); },
|
||||||
|
[&](const SingleDerivedPathBuilt & p) { waitForPath(*p.drvPath); },
|
||||||
|
},
|
||||||
|
path.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvalState::waitForAllPaths()
|
||||||
|
{
|
||||||
|
asyncPathWriter->waitForAllPaths();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ class StorePath;
|
||||||
struct SingleDerivedPath;
|
struct SingleDerivedPath;
|
||||||
enum RepairFlag : bool;
|
enum RepairFlag : bool;
|
||||||
struct MemorySourceAccessor;
|
struct MemorySourceAccessor;
|
||||||
|
struct AsyncPathWriter;
|
||||||
|
|
||||||
namespace eval_cache {
|
namespace eval_cache {
|
||||||
class EvalCache;
|
class EvalCache;
|
||||||
|
|
@ -320,6 +321,8 @@ public:
|
||||||
std::list<DebugTrace> debugTraces;
|
std::list<DebugTrace> debugTraces;
|
||||||
std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||||
|
|
||||||
|
ref<AsyncPathWriter> asyncPathWriter;
|
||||||
|
|
||||||
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
|
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
|
||||||
{
|
{
|
||||||
auto i = exprEnvs.find(&expr);
|
auto i = exprEnvs.find(&expr);
|
||||||
|
|
@ -907,6 +910,10 @@ public:
|
||||||
|
|
||||||
DocComment getDocCommentForPos(PosIdx pos);
|
DocComment getDocCommentForPos(PosIdx pos);
|
||||||
|
|
||||||
|
void waitForPath(const StorePath & path);
|
||||||
|
void waitForPath(const SingleDerivedPath & path);
|
||||||
|
void waitForAllPaths();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
|
||||||
|
|
||||||
for (auto & c : context) {
|
for (auto & c : context) {
|
||||||
auto ensureValid = [&](const StorePath & p) {
|
auto ensureValid = [&](const StorePath & p) {
|
||||||
|
waitForPath(p);
|
||||||
if (!store->isValidPath(p))
|
if (!store->isValidPath(p))
|
||||||
error<InvalidPathError>(store->printStorePath(p)).debugThrow();
|
error<InvalidPathError>(store->printStorePath(p)).debugThrow();
|
||||||
};
|
};
|
||||||
|
|
@ -291,6 +292,7 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
|
||||||
if (!state.store->isStorePath(path2))
|
if (!state.store->isStorePath(path2))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
auto storePath = state.store->parseStorePath(path2);
|
auto storePath = state.store->parseStorePath(path2);
|
||||||
|
state.waitForPath(storePath);
|
||||||
if (!(state.store->isValidPath(storePath) && isDerivation(path2)))
|
if (!(state.store->isValidPath(storePath) && isDerivation(path2)))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return storePath;
|
return storePath;
|
||||||
|
|
@ -1583,6 +1585,8 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
|
||||||
[&](const NixStringContextElem::DrvDeep & d) {
|
[&](const NixStringContextElem::DrvDeep & d) {
|
||||||
/* !!! This doesn't work if readOnlyMode is set. */
|
/* !!! This doesn't work if readOnlyMode is set. */
|
||||||
StorePathSet refs;
|
StorePathSet refs;
|
||||||
|
// FIXME: don't need to wait, we only need the references.
|
||||||
|
state.waitForPath(d.drvPath);
|
||||||
state.store->computeFSClosure(d.drvPath, refs);
|
state.store->computeFSClosure(d.drvPath, refs);
|
||||||
for (auto & j : refs) {
|
for (auto & j : refs) {
|
||||||
drv.inputSrcs.insert(j);
|
drv.inputSrcs.insert(j);
|
||||||
|
|
@ -1707,7 +1711,7 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write the resulting term into the Nix store directory. */
|
/* Write the resulting term into the Nix store directory. */
|
||||||
auto drvPath = writeDerivation(*state.store, drv, state.repair);
|
auto drvPath = writeDerivation(*state.store, *state.asyncPathWriter, drv, state.repair);
|
||||||
auto drvPathS = state.store->printStorePath(drvPath);
|
auto drvPathS = state.store->printStorePath(drvPath);
|
||||||
|
|
||||||
printMsg(lvlChatty, "instantiated '%1%' -> '%2%'", drvName, drvPathS);
|
printMsg(lvlChatty, "instantiated '%1%' -> '%2%'", drvName, drvPathS);
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
|
||||||
NixStringContext context2;
|
NixStringContext context2;
|
||||||
for (auto && c : context) {
|
for (auto && c : context) {
|
||||||
if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c.raw)) {
|
if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c.raw)) {
|
||||||
|
state.waitForPath(ptr->drvPath); // FIXME: why?
|
||||||
context2.emplace(NixStringContextElem::Opaque{.path = ptr->drvPath});
|
context2.emplace(NixStringContextElem::Opaque{.path = ptr->drvPath});
|
||||||
} else {
|
} else {
|
||||||
/* Can reuse original item */
|
/* Can reuse original item */
|
||||||
|
|
|
||||||
151
src/libstore/async-path-writer.cc
Normal file
151
src/libstore/async-path-writer.cc
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
#include "nix/store/async-path-writer.hh"
|
||||||
|
#include "nix/util/archive.hh"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct AsyncPathWriterImpl : AsyncPathWriter
|
||||||
|
{
|
||||||
|
ref<Store> store;
|
||||||
|
|
||||||
|
struct Item
|
||||||
|
{
|
||||||
|
StorePath storePath;
|
||||||
|
std::string contents;
|
||||||
|
std::string name;
|
||||||
|
Hash hash;
|
||||||
|
StorePathSet references;
|
||||||
|
RepairFlag repair;
|
||||||
|
std::promise<void> promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
std::vector<Item> items;
|
||||||
|
std::unordered_map<StorePath, std::shared_future<void>> futures;
|
||||||
|
bool quit = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
Sync<State> state_;
|
||||||
|
|
||||||
|
std::thread workerThread;
|
||||||
|
|
||||||
|
std::condition_variable wakeupCV;
|
||||||
|
|
||||||
|
AsyncPathWriterImpl(ref<Store> store)
|
||||||
|
: store(store)
|
||||||
|
{
|
||||||
|
workerThread = std::thread([&]() {
|
||||||
|
while (true) {
|
||||||
|
std::vector<Item> items;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto state(state_.lock());
|
||||||
|
while (!state->quit && state->items.empty())
|
||||||
|
state.wait(wakeupCV);
|
||||||
|
if (state->items.empty() && state->quit)
|
||||||
|
return;
|
||||||
|
std::swap(items, state->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
writePaths(items);
|
||||||
|
for (auto & item : items)
|
||||||
|
item.promise.set_value();
|
||||||
|
} catch (...) {
|
||||||
|
for (auto & item : items)
|
||||||
|
item.promise.set_exception(std::current_exception());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncPathWriterImpl()
|
||||||
|
{
|
||||||
|
state_.lock()->quit = true;
|
||||||
|
wakeupCV.notify_all();
|
||||||
|
workerThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePath
|
||||||
|
addPath(std::string contents, std::string name, StorePathSet references, RepairFlag repair, bool readOnly) override
|
||||||
|
{
|
||||||
|
auto hash = hashString(HashAlgorithm::SHA256, contents);
|
||||||
|
|
||||||
|
auto storePath = store->makeFixedOutputPathFromCA(
|
||||||
|
name,
|
||||||
|
TextInfo{
|
||||||
|
.hash = hash,
|
||||||
|
.references = references,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!readOnly) {
|
||||||
|
auto state(state_.lock());
|
||||||
|
std::promise<void> promise;
|
||||||
|
state->futures.insert_or_assign(storePath, promise.get_future());
|
||||||
|
state->items.push_back(
|
||||||
|
Item{
|
||||||
|
.storePath = storePath,
|
||||||
|
.contents = std::move(contents),
|
||||||
|
.name = std::move(name),
|
||||||
|
.hash = hash,
|
||||||
|
.references = std::move(references),
|
||||||
|
.repair = repair,
|
||||||
|
.promise = std::move(promise),
|
||||||
|
});
|
||||||
|
wakeupCV.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitForPath(const StorePath & path) override
|
||||||
|
{
|
||||||
|
auto future = ({
|
||||||
|
auto state = state_.lock();
|
||||||
|
auto i = state->futures.find(path);
|
||||||
|
if (i == state->futures.end())
|
||||||
|
return;
|
||||||
|
i->second;
|
||||||
|
});
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitForAllPaths() override
|
||||||
|
{
|
||||||
|
auto futures = ({
|
||||||
|
auto state(state_.lock());
|
||||||
|
std::move(state->futures);
|
||||||
|
});
|
||||||
|
for (auto & future : futures)
|
||||||
|
future.second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePaths(const std::vector<Item> & items)
|
||||||
|
{
|
||||||
|
// FIXME: use addMultipeToStore() once it doesn't require a
|
||||||
|
// NAR hash from the client for CA objects.
|
||||||
|
|
||||||
|
for (auto & item : items) {
|
||||||
|
StringSource source(item.contents);
|
||||||
|
auto storePath = store->addToStoreFromDump(
|
||||||
|
source,
|
||||||
|
item.storePath.name(),
|
||||||
|
FileSerialisationMethod::Flat,
|
||||||
|
ContentAddressMethod::Raw::Text,
|
||||||
|
HashAlgorithm::SHA256,
|
||||||
|
item.references,
|
||||||
|
item.repair);
|
||||||
|
assert(storePath == item.storePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ref<AsyncPathWriter> AsyncPathWriter::make(ref<Store> store)
|
||||||
|
{
|
||||||
|
return make_ref<AsyncPathWriterImpl>(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nix
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "nix/store/common-protocol-impl.hh"
|
#include "nix/store/common-protocol-impl.hh"
|
||||||
#include "nix/util/strings-inline.hh"
|
#include "nix/util/strings-inline.hh"
|
||||||
#include "nix/util/json-utils.hh"
|
#include "nix/util/json-utils.hh"
|
||||||
|
#include "nix/store/async-path-writer.hh"
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
@ -133,6 +134,20 @@ StorePath writeDerivation(Store & store, const Derivation & drv, RepairFlag repa
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StorePath writeDerivation(
|
||||||
|
Store & store, AsyncPathWriter & asyncPathWriter, const Derivation & drv, RepairFlag repair, bool readOnly)
|
||||||
|
{
|
||||||
|
auto references = drv.inputSrcs;
|
||||||
|
for (auto & i : drv.inputDrvs.map)
|
||||||
|
references.insert(i.first);
|
||||||
|
return asyncPathWriter.addPath(
|
||||||
|
drv.unparse(store, false),
|
||||||
|
std::string(drv.name) + drvExtension,
|
||||||
|
references,
|
||||||
|
repair,
|
||||||
|
readOnly || settings.readOnlyMode);
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
/**
|
/**
|
||||||
* This mimics std::istream to some extent. We use this much smaller implementation
|
* This mimics std::istream to some extent. We use this much smaller implementation
|
||||||
|
|
|
||||||
19
src/libstore/include/nix/store/async-path-writer.hh
Normal file
19
src/libstore/include/nix/store/async-path-writer.hh
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nix/store/store-api.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct AsyncPathWriter
|
||||||
|
{
|
||||||
|
virtual StorePath addPath(
|
||||||
|
std::string contents, std::string name, StorePathSet references, RepairFlag repair, bool readOnly = false) = 0;
|
||||||
|
|
||||||
|
virtual void waitForPath(const StorePath & path) = 0;
|
||||||
|
|
||||||
|
virtual void waitForAllPaths() = 0;
|
||||||
|
|
||||||
|
static ref<AsyncPathWriter> make(ref<Store> store);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace nix
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct StoreDirConfig;
|
struct StoreDirConfig;
|
||||||
|
struct AsyncPathWriter;
|
||||||
|
|
||||||
/* Abstract syntax of derivations. */
|
/* Abstract syntax of derivations. */
|
||||||
|
|
||||||
|
|
@ -412,6 +413,16 @@ class Store;
|
||||||
*/
|
*/
|
||||||
StorePath writeDerivation(Store & store, const Derivation & drv, RepairFlag repair = NoRepair, bool readOnly = false);
|
StorePath writeDerivation(Store & store, const Derivation & drv, RepairFlag repair = NoRepair, bool readOnly = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously write a derivation to the Nix store, and return its path.
|
||||||
|
*/
|
||||||
|
StorePath writeDerivation(
|
||||||
|
Store & store,
|
||||||
|
AsyncPathWriter & asyncPathWriter,
|
||||||
|
const Derivation & drv,
|
||||||
|
RepairFlag repair = NoRepair,
|
||||||
|
bool readOnly = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a derivation from a file.
|
* Read a derivation from a file.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ config_pub_h = configure_file(
|
||||||
)
|
)
|
||||||
|
|
||||||
headers = [ config_pub_h ] + files(
|
headers = [ config_pub_h ] + files(
|
||||||
|
'async-path-writer.hh',
|
||||||
'binary-cache-store.hh',
|
'binary-cache-store.hh',
|
||||||
'build-result.hh',
|
'build-result.hh',
|
||||||
'build/derivation-building-goal.hh',
|
'build/derivation-building-goal.hh',
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,7 @@ config_priv_h = configure_file(
|
||||||
subdir('nix-meson-build-support/common')
|
subdir('nix-meson-build-support/common')
|
||||||
|
|
||||||
sources = files(
|
sources = files(
|
||||||
|
'async-path-writer.cc',
|
||||||
'binary-cache-store.cc',
|
'binary-cache-store.cc',
|
||||||
'build-result.cc',
|
'build-result.cc',
|
||||||
'build/derivation-building-goal.cc',
|
'build/derivation-building-goal.cc',
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ UnresolvedApp InstallableValue::toApp(EvalState & state)
|
||||||
std::visit(
|
std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
[&](const NixStringContextElem::DrvDeep & d) -> DerivedPath {
|
[&](const NixStringContextElem::DrvDeep & d) -> DerivedPath {
|
||||||
|
state.waitForPath(d.drvPath);
|
||||||
/* We want all outputs of the drv */
|
/* We want all outputs of the drv */
|
||||||
return DerivedPath::Built{
|
return DerivedPath::Built{
|
||||||
.drvPath = makeConstantStorePathRef(d.drvPath),
|
.drvPath = makeConstantStorePathRef(d.drvPath),
|
||||||
|
|
@ -81,6 +82,7 @@ UnresolvedApp InstallableValue::toApp(EvalState & state)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[&](const NixStringContextElem::Built & b) -> DerivedPath {
|
[&](const NixStringContextElem::Built & b) -> DerivedPath {
|
||||||
|
state.waitForPath(*b.drvPath);
|
||||||
return DerivedPath::Built{
|
return DerivedPath::Built{
|
||||||
.drvPath = b.drvPath,
|
.drvPath = b.drvPath,
|
||||||
.outputs = OutputsSpec::Names{b.output},
|
.outputs = OutputsSpec::Names{b.output},
|
||||||
|
|
|
||||||
|
|
@ -451,7 +451,9 @@ static void main_nix_build(int argc, char ** argv)
|
||||||
throw UsageError("nix-shell requires a single derivation");
|
throw UsageError("nix-shell requires a single derivation");
|
||||||
|
|
||||||
auto & packageInfo = drvs.front();
|
auto & packageInfo = drvs.front();
|
||||||
auto drv = evalStore->derivationFromPath(packageInfo.requireDrvPath());
|
auto drvPath = packageInfo.requireDrvPath();
|
||||||
|
state->waitForPath(drvPath);
|
||||||
|
auto drv = evalStore->derivationFromPath(drvPath);
|
||||||
|
|
||||||
std::vector<DerivedPath> pathsToBuild;
|
std::vector<DerivedPath> pathsToBuild;
|
||||||
RealisedPath::Set pathsToCopy;
|
RealisedPath::Set pathsToCopy;
|
||||||
|
|
@ -475,6 +477,7 @@ static void main_nix_build(int argc, char ** argv)
|
||||||
throw Error("the 'bashInteractive' attribute in <nixpkgs> did not evaluate to a derivation");
|
throw Error("the 'bashInteractive' attribute in <nixpkgs> did not evaluate to a derivation");
|
||||||
|
|
||||||
auto bashDrv = drv->requireDrvPath();
|
auto bashDrv = drv->requireDrvPath();
|
||||||
|
state->waitForPath(bashDrv);
|
||||||
pathsToBuild.push_back(
|
pathsToBuild.push_back(
|
||||||
DerivedPath::Built{
|
DerivedPath::Built{
|
||||||
.drvPath = makeConstantStorePathRef(bashDrv),
|
.drvPath = makeConstantStorePathRef(bashDrv),
|
||||||
|
|
@ -683,6 +686,7 @@ static void main_nix_build(int argc, char ** argv)
|
||||||
|
|
||||||
for (auto & packageInfo : drvs) {
|
for (auto & packageInfo : drvs) {
|
||||||
auto drvPath = packageInfo.requireDrvPath();
|
auto drvPath = packageInfo.requireDrvPath();
|
||||||
|
state->waitForPath(drvPath);
|
||||||
|
|
||||||
auto outputName = packageInfo.queryOutputName();
|
auto outputName = packageInfo.queryOutputName();
|
||||||
if (outputName == "")
|
if (outputName == "")
|
||||||
|
|
|
||||||
|
|
@ -746,6 +746,8 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
drv.setName(globals.forceName);
|
drv.setName(globals.forceName);
|
||||||
|
|
||||||
auto drvPath = drv.queryDrvPath();
|
auto drvPath = drv.queryDrvPath();
|
||||||
|
if (drvPath)
|
||||||
|
globals.state->waitForPath(*drvPath);
|
||||||
std::vector<DerivedPath> paths{
|
std::vector<DerivedPath> paths{
|
||||||
drvPath ? (DerivedPath) (DerivedPath::Built{
|
drvPath ? (DerivedPath) (DerivedPath::Built{
|
||||||
.drvPath = makeConstantStorePathRef(*drvPath),
|
.drvPath = makeConstantStorePathRef(*drvPath),
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,10 @@ bool createUserEnv(
|
||||||
exist already. */
|
exist already. */
|
||||||
std::vector<StorePathWithOutputs> drvsToBuild;
|
std::vector<StorePathWithOutputs> drvsToBuild;
|
||||||
for (auto & i : elems)
|
for (auto & i : elems)
|
||||||
if (auto drvPath = i.queryDrvPath())
|
if (auto drvPath = i.queryDrvPath()) {
|
||||||
|
state.waitForPath(*drvPath);
|
||||||
drvsToBuild.push_back({*drvPath});
|
drvsToBuild.push_back({*drvPath});
|
||||||
|
}
|
||||||
|
|
||||||
debug("building user environment dependencies");
|
debug("building user environment dependencies");
|
||||||
state.store->buildPaths(toDerivedPaths(drvsToBuild), state.repair ? bmRepair : bmNormal);
|
state.store->buildPaths(toDerivedPaths(drvsToBuild), state.repair ? bmRepair : bmNormal);
|
||||||
|
|
@ -151,6 +153,7 @@ bool createUserEnv(
|
||||||
debug("building user environment");
|
debug("building user environment");
|
||||||
std::vector<StorePathWithOutputs> topLevelDrvs;
|
std::vector<StorePathWithOutputs> topLevelDrvs;
|
||||||
topLevelDrvs.push_back({topLevelDrv});
|
topLevelDrvs.push_back({topLevelDrv});
|
||||||
|
state.waitForPath(topLevelDrv);
|
||||||
state.store->buildPaths(toDerivedPaths(topLevelDrvs), state.repair ? bmRepair : bmNormal);
|
state.store->buildPaths(toDerivedPaths(topLevelDrvs), state.repair ? bmRepair : bmNormal);
|
||||||
|
|
||||||
/* Switch the current user environment to the output path. */
|
/* Switch the current user environment to the output path. */
|
||||||
|
|
|
||||||
|
|
@ -48,3 +48,11 @@ nix build --no-link "$flake1Dir#stack-depth"
|
||||||
expect 1 nix build "$flake1Dir#ifd" --option allow-import-from-derivation false 2>&1 \
|
expect 1 nix build "$flake1Dir#ifd" --option allow-import-from-derivation false 2>&1 \
|
||||||
| grepQuiet 'error: cannot build .* during evaluation because the option '\''allow-import-from-derivation'\'' is disabled'
|
| grepQuiet 'error: cannot build .* during evaluation because the option '\''allow-import-from-derivation'\'' is disabled'
|
||||||
nix build --no-link "$flake1Dir#ifd"
|
nix build --no-link "$flake1Dir#ifd"
|
||||||
|
|
||||||
|
# Test that a store derivation is recreated when it has been deleted
|
||||||
|
# but the corresponding attribute is still cached.
|
||||||
|
if ! isTestOnNixOS; then
|
||||||
|
nix build "$flake1Dir#drv"
|
||||||
|
clearStore
|
||||||
|
nix build "$flake1Dir#drv"
|
||||||
|
fi
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue