mirror of
https://github.com/NixOS/nix
synced 2025-06-27 04:21:16 +02:00
Merge pull request #12992 from DeterminateSystems/input-cache
Rename FlakeCache to InputCache and move it to libfetchers
This commit is contained in:
commit
22c928f9c0
8 changed files with 140 additions and 79 deletions
|
@ -19,6 +19,7 @@
|
||||||
#include "nix/util/url.hh"
|
#include "nix/util/url.hh"
|
||||||
#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 "parser-tab.hh"
|
#include "parser-tab.hh"
|
||||||
|
|
||||||
|
@ -310,6 +311,7 @@ EvalState::EvalState(
|
||||||
)}
|
)}
|
||||||
, store(store)
|
, store(store)
|
||||||
, buildStore(buildStore ? buildStore : store)
|
, buildStore(buildStore ? buildStore : store)
|
||||||
|
, inputCache(fetchers::InputCache::create())
|
||||||
, debugRepl(nullptr)
|
, debugRepl(nullptr)
|
||||||
, debugStop(false)
|
, debugStop(false)
|
||||||
, trylevel(0)
|
, trylevel(0)
|
||||||
|
@ -1152,6 +1154,7 @@ void EvalState::resetFileCache()
|
||||||
{
|
{
|
||||||
fileEvalCache.clear();
|
fileEvalCache.clear();
|
||||||
fileParseCache.clear();
|
fileParseCache.clear();
|
||||||
|
inputCache->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,10 @@ namespace nix {
|
||||||
constexpr size_t maxPrimOpArity = 8;
|
constexpr size_t maxPrimOpArity = 8;
|
||||||
|
|
||||||
class Store;
|
class Store;
|
||||||
namespace fetchers { struct Settings; }
|
namespace fetchers {
|
||||||
|
struct Settings;
|
||||||
|
struct InputCache;
|
||||||
|
}
|
||||||
struct EvalSettings;
|
struct EvalSettings;
|
||||||
class EvalState;
|
class EvalState;
|
||||||
class StorePath;
|
class StorePath;
|
||||||
|
@ -300,6 +303,8 @@ public:
|
||||||
|
|
||||||
RootValue vImportedDrvToDerivation = nullptr;
|
RootValue vImportedDrvToDerivation = nullptr;
|
||||||
|
|
||||||
|
ref<fetchers::InputCache> inputCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debugger
|
* Debugger
|
||||||
*/
|
*/
|
||||||
|
|
31
src/libfetchers/include/nix/fetchers/input-cache.hh
Normal file
31
src/libfetchers/include/nix/fetchers/input-cache.hh
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include "fetchers.hh"
|
||||||
|
|
||||||
|
namespace nix::fetchers {
|
||||||
|
|
||||||
|
struct InputCache
|
||||||
|
{
|
||||||
|
struct CachedResult
|
||||||
|
{
|
||||||
|
ref<SourceAccessor> accessor;
|
||||||
|
Input resolvedInput;
|
||||||
|
Input lockedInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
CachedResult getAccessor(ref<Store> store, const Input & originalInput, bool useRegistries);
|
||||||
|
|
||||||
|
struct CachedInput
|
||||||
|
{
|
||||||
|
Input lockedInput;
|
||||||
|
ref<SourceAccessor> accessor;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual std::optional<CachedInput> lookup(const Input & originalInput) const = 0;
|
||||||
|
|
||||||
|
virtual void upsert(Input key, CachedInput cachedInput) = 0;
|
||||||
|
|
||||||
|
virtual void clear() = 0;
|
||||||
|
|
||||||
|
static ref<InputCache> create();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ headers = files(
|
||||||
'filtering-source-accessor.hh',
|
'filtering-source-accessor.hh',
|
||||||
'git-lfs-fetch.hh',
|
'git-lfs-fetch.hh',
|
||||||
'git-utils.hh',
|
'git-utils.hh',
|
||||||
|
'input-cache.hh',
|
||||||
'registry.hh',
|
'registry.hh',
|
||||||
'store-path-accessor.hh',
|
'store-path-accessor.hh',
|
||||||
'tarball.hh',
|
'tarball.hh',
|
||||||
|
|
80
src/libfetchers/input-cache.cc
Normal file
80
src/libfetchers/input-cache.cc
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#include "nix/fetchers/input-cache.hh"
|
||||||
|
#include "nix/fetchers/registry.hh"
|
||||||
|
#include "nix/util/sync.hh"
|
||||||
|
#include "nix/util/source-path.hh"
|
||||||
|
|
||||||
|
namespace nix::fetchers {
|
||||||
|
|
||||||
|
InputCache::CachedResult InputCache::getAccessor(ref<Store> store, const Input & originalInput, bool useRegistries)
|
||||||
|
{
|
||||||
|
auto fetched = lookup(originalInput);
|
||||||
|
Input resolvedInput = originalInput;
|
||||||
|
|
||||||
|
if (!fetched) {
|
||||||
|
if (originalInput.isDirect()) {
|
||||||
|
auto [accessor, lockedInput] = originalInput.getAccessor(store);
|
||||||
|
fetched.emplace(CachedInput{.lockedInput = lockedInput, .accessor = accessor});
|
||||||
|
} else {
|
||||||
|
if (useRegistries) {
|
||||||
|
auto [res, extraAttrs] =
|
||||||
|
lookupInRegistries(store, originalInput, [](fetchers::Registry::RegistryType type) {
|
||||||
|
/* Only use the global registry and CLI flags
|
||||||
|
to resolve indirect flakerefs. */
|
||||||
|
return type == fetchers::Registry::Flag || type == fetchers::Registry::Global;
|
||||||
|
});
|
||||||
|
resolvedInput = std::move(res);
|
||||||
|
fetched = lookup(resolvedInput);
|
||||||
|
if (!fetched) {
|
||||||
|
auto [accessor, lockedInput] = resolvedInput.getAccessor(store);
|
||||||
|
fetched.emplace(CachedInput{.lockedInput = lockedInput, .accessor = accessor});
|
||||||
|
}
|
||||||
|
upsert(resolvedInput, *fetched);
|
||||||
|
} else {
|
||||||
|
throw Error(
|
||||||
|
"'%s' is an indirect flake reference, but registry lookups are not allowed",
|
||||||
|
originalInput.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upsert(originalInput, *fetched);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("got tree '%s' from '%s'", fetched->accessor, fetched->lockedInput.to_string());
|
||||||
|
|
||||||
|
return {fetched->accessor, resolvedInput, fetched->lockedInput};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InputCacheImpl : InputCache
|
||||||
|
{
|
||||||
|
Sync<std::map<Input, CachedInput>> cache_;
|
||||||
|
|
||||||
|
std::optional<CachedInput> lookup(const Input & originalInput) const override
|
||||||
|
{
|
||||||
|
auto cache(cache_.readLock());
|
||||||
|
auto i = cache->find(originalInput);
|
||||||
|
if (i == cache->end())
|
||||||
|
return std::nullopt;
|
||||||
|
debug(
|
||||||
|
"mapping '%s' to previously seen input '%s' -> '%s",
|
||||||
|
originalInput.to_string(),
|
||||||
|
i->first.to_string(),
|
||||||
|
i->second.lockedInput.to_string());
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void upsert(Input key, CachedInput cachedInput) override
|
||||||
|
{
|
||||||
|
cache_.lock()->insert_or_assign(std::move(key), std::move(cachedInput));
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() override
|
||||||
|
{
|
||||||
|
cache_.lock()->clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ref<InputCache> InputCache::create()
|
||||||
|
{
|
||||||
|
return make_ref<InputCacheImpl>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ sources = files(
|
||||||
'git.cc',
|
'git.cc',
|
||||||
'github.cc',
|
'github.cc',
|
||||||
'indirect.cc',
|
'indirect.cc',
|
||||||
|
'input-cache.cc',
|
||||||
'mercurial.cc',
|
'mercurial.cc',
|
||||||
'path.cc',
|
'path.cc',
|
||||||
'registry.cc',
|
'registry.cc',
|
||||||
|
|
|
@ -177,6 +177,7 @@ nix_locked_flake * nix_flake_lock(
|
||||||
{
|
{
|
||||||
nix_clear_err(context);
|
nix_clear_err(context);
|
||||||
try {
|
try {
|
||||||
|
eval_state->state.resetFileCache();
|
||||||
auto lockedFlake = nix::make_ref<nix::flake::LockedFlake>(nix::flake::lockFlake(
|
auto lockedFlake = nix::make_ref<nix::flake::LockedFlake>(nix::flake::lockFlake(
|
||||||
*flakeSettings->settings, eval_state->state, *flakeReference->flakeRef, *flags->lockFlags));
|
*flakeSettings->settings, eval_state->state, *flakeReference->flakeRef, *flags->lockFlags));
|
||||||
return new nix_locked_flake{lockedFlake};
|
return new nix_locked_flake{lockedFlake};
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "nix/store/local-fs-store.hh"
|
#include "nix/store/local-fs-store.hh"
|
||||||
#include "nix/fetchers/fetch-to-store.hh"
|
#include "nix/fetchers/fetch-to-store.hh"
|
||||||
#include "nix/util/memory-source-accessor.hh"
|
#include "nix/util/memory-source-accessor.hh"
|
||||||
|
#include "nix/fetchers/input-cache.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
@ -23,66 +24,6 @@ using namespace flake;
|
||||||
|
|
||||||
namespace flake {
|
namespace flake {
|
||||||
|
|
||||||
struct FetchedFlake
|
|
||||||
{
|
|
||||||
FlakeRef lockedRef;
|
|
||||||
ref<SourceAccessor> accessor;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::map<FlakeRef, FetchedFlake> FlakeCache;
|
|
||||||
|
|
||||||
static std::optional<FetchedFlake> lookupInFlakeCache(
|
|
||||||
const FlakeCache & flakeCache,
|
|
||||||
const FlakeRef & flakeRef)
|
|
||||||
{
|
|
||||||
auto i = flakeCache.find(flakeRef);
|
|
||||||
if (i == flakeCache.end()) return std::nullopt;
|
|
||||||
debug("mapping '%s' to previously seen input '%s' -> '%s",
|
|
||||||
flakeRef, i->first, i->second.lockedRef);
|
|
||||||
return i->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::tuple<ref<SourceAccessor>, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
|
||||||
EvalState & state,
|
|
||||||
const FlakeRef & originalRef,
|
|
||||||
bool useRegistries,
|
|
||||||
FlakeCache & flakeCache)
|
|
||||||
{
|
|
||||||
auto fetched = lookupInFlakeCache(flakeCache, originalRef);
|
|
||||||
FlakeRef resolvedRef = originalRef;
|
|
||||||
|
|
||||||
if (!fetched) {
|
|
||||||
if (originalRef.input.isDirect()) {
|
|
||||||
auto [accessor, lockedRef] = originalRef.lazyFetch(state.store);
|
|
||||||
fetched.emplace(FetchedFlake{.lockedRef = lockedRef, .accessor = accessor});
|
|
||||||
} else {
|
|
||||||
if (useRegistries) {
|
|
||||||
resolvedRef = originalRef.resolve(
|
|
||||||
state.store,
|
|
||||||
[](fetchers::Registry::RegistryType type) {
|
|
||||||
/* Only use the global registry and CLI flags
|
|
||||||
to resolve indirect flakerefs. */
|
|
||||||
return type == fetchers::Registry::Flag || type == fetchers::Registry::Global;
|
|
||||||
});
|
|
||||||
fetched = lookupInFlakeCache(flakeCache, originalRef);
|
|
||||||
if (!fetched) {
|
|
||||||
auto [accessor, lockedRef] = resolvedRef.lazyFetch(state.store);
|
|
||||||
fetched.emplace(FetchedFlake{.lockedRef = lockedRef, .accessor = accessor});
|
|
||||||
}
|
|
||||||
flakeCache.insert_or_assign(resolvedRef, *fetched);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", originalRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flakeCache.insert_or_assign(originalRef, *fetched);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("got tree '%s' from '%s'", fetched->accessor, fetched->lockedRef);
|
|
||||||
|
|
||||||
return {fetched->accessor, resolvedRef, fetched->lockedRef};
|
|
||||||
}
|
|
||||||
|
|
||||||
static StorePath copyInputToStore(
|
static StorePath copyInputToStore(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
fetchers::Input & input,
|
fetchers::Input & input,
|
||||||
|
@ -397,15 +338,16 @@ static Flake getFlake(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const FlakeRef & originalRef,
|
const FlakeRef & originalRef,
|
||||||
bool useRegistries,
|
bool useRegistries,
|
||||||
FlakeCache & flakeCache,
|
|
||||||
const InputAttrPath & lockRootAttrPath)
|
const InputAttrPath & lockRootAttrPath)
|
||||||
{
|
{
|
||||||
// Fetch a lazy tree first.
|
// Fetch a lazy tree first.
|
||||||
auto [accessor, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
auto cachedInput = state.inputCache->getAccessor(state.store, originalRef.input, useRegistries);
|
||||||
state, originalRef, useRegistries, flakeCache);
|
|
||||||
|
auto resolvedRef = FlakeRef(std::move(cachedInput.resolvedInput), originalRef.subdir);
|
||||||
|
auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), originalRef.subdir);
|
||||||
|
|
||||||
// Parse/eval flake.nix to get at the input.self attributes.
|
// Parse/eval flake.nix to get at the input.self attributes.
|
||||||
auto flake = readFlake(state, originalRef, resolvedRef, lockedRef, {accessor}, lockRootAttrPath);
|
auto flake = readFlake(state, originalRef, resolvedRef, lockedRef, {cachedInput.accessor}, lockRootAttrPath);
|
||||||
|
|
||||||
// Re-fetch the tree if necessary.
|
// Re-fetch the tree if necessary.
|
||||||
auto newLockedRef = applySelfAttrs(lockedRef, flake);
|
auto newLockedRef = applySelfAttrs(lockedRef, flake);
|
||||||
|
@ -414,14 +356,13 @@ static Flake getFlake(
|
||||||
debug("refetching input '%s' due to self attribute", newLockedRef);
|
debug("refetching input '%s' due to self attribute", newLockedRef);
|
||||||
// FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'.
|
// FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'.
|
||||||
newLockedRef.input.attrs.erase("narHash");
|
newLockedRef.input.attrs.erase("narHash");
|
||||||
auto [accessor2, resolvedRef2, lockedRef2] = fetchOrSubstituteTree(
|
auto cachedInput2 = state.inputCache->getAccessor(state.store, newLockedRef.input, useRegistries);
|
||||||
state, newLockedRef, false, flakeCache);
|
cachedInput.accessor = cachedInput2.accessor;
|
||||||
accessor = accessor2;
|
lockedRef = FlakeRef(std::move(cachedInput2.lockedInput), newLockedRef.subdir);
|
||||||
lockedRef = lockedRef2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the tree to the store.
|
// Copy the tree to the store.
|
||||||
auto storePath = copyInputToStore(state, lockedRef.input, originalRef.input, accessor);
|
auto storePath = copyInputToStore(state, lockedRef.input, originalRef.input, cachedInput.accessor);
|
||||||
|
|
||||||
// Re-parse flake.nix from the store.
|
// Re-parse flake.nix from the store.
|
||||||
return readFlake(state, originalRef, resolvedRef, lockedRef, state.storePath(storePath), lockRootAttrPath);
|
return readFlake(state, originalRef, resolvedRef, lockedRef, state.storePath(storePath), lockRootAttrPath);
|
||||||
|
@ -429,8 +370,7 @@ static Flake getFlake(
|
||||||
|
|
||||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries)
|
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries)
|
||||||
{
|
{
|
||||||
FlakeCache flakeCache;
|
return getFlake(state, originalRef, useRegistries, {});
|
||||||
return getFlake(state, originalRef, useRegistries, flakeCache, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static LockFile readLockFile(
|
static LockFile readLockFile(
|
||||||
|
@ -452,11 +392,9 @@ LockedFlake lockFlake(
|
||||||
{
|
{
|
||||||
experimentalFeatureSettings.require(Xp::Flakes);
|
experimentalFeatureSettings.require(Xp::Flakes);
|
||||||
|
|
||||||
FlakeCache flakeCache;
|
|
||||||
|
|
||||||
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
|
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
|
||||||
|
|
||||||
auto flake = getFlake(state, topRef, useRegistries, flakeCache, {});
|
auto flake = getFlake(state, topRef, useRegistries, {});
|
||||||
|
|
||||||
if (lockFlags.applyNixConfig) {
|
if (lockFlags.applyNixConfig) {
|
||||||
flake.config.apply(settings);
|
flake.config.apply(settings);
|
||||||
|
@ -631,7 +569,7 @@ LockedFlake lockFlake(
|
||||||
if (auto resolvedPath = resolveRelativePath()) {
|
if (auto resolvedPath = resolveRelativePath()) {
|
||||||
return readFlake(state, ref, ref, ref, *resolvedPath, inputAttrPath);
|
return readFlake(state, ref, ref, ref, *resolvedPath, inputAttrPath);
|
||||||
} else {
|
} else {
|
||||||
return getFlake(state, ref, useRegistries, flakeCache, inputAttrPath);
|
return getFlake(state, ref, useRegistries, inputAttrPath);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -779,11 +717,12 @@ LockedFlake lockFlake(
|
||||||
if (auto resolvedPath = resolveRelativePath()) {
|
if (auto resolvedPath = resolveRelativePath()) {
|
||||||
return {*resolvedPath, *input.ref};
|
return {*resolvedPath, *input.ref};
|
||||||
} else {
|
} else {
|
||||||
auto [accessor, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
auto cachedInput = state.inputCache->getAccessor(state.store, input.ref->input, useRegistries);
|
||||||
state, *input.ref, useRegistries, flakeCache);
|
|
||||||
|
auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), input.ref->subdir);
|
||||||
|
|
||||||
// FIXME: allow input to be lazy.
|
// FIXME: allow input to be lazy.
|
||||||
auto storePath = copyInputToStore(state, lockedRef.input, input.ref->input, accessor);
|
auto storePath = copyInputToStore(state, lockedRef.input, input.ref->input, cachedInput.accessor);
|
||||||
|
|
||||||
return {state.storePath(storePath), lockedRef};
|
return {state.storePath(storePath), lockedRef};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue