1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-10 12:36:01 +01:00

fetchTree: Return a path instead of a store path

Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
Co-authored-by: Robert Hensing <robert@roberthensing.nl>
This commit is contained in:
Tom Bereknyei 2024-08-23 23:52:21 -04:00
parent fa49d2e356
commit 50b00b0194
16 changed files with 165 additions and 118 deletions

View file

@ -32,8 +32,7 @@ EvalSettings evalSettings {
// FIXME `parseFlakeRef` should take a `std::string_view`. // FIXME `parseFlakeRef` should take a `std::string_view`.
auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false);
debug("fetching flake search path element '%s''", rest); debug("fetching flake search path element '%s''", rest);
auto storePath = flakeRef.resolve(store).fetchTree(store).first; return flakeRef.resolve(store).lazyFetch(store).first;
return store->toRealPath(storePath);
}, },
}, },
}, },
@ -174,15 +173,15 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
state.store, state.store,
state.fetchSettings, state.fetchSettings,
EvalSettings::resolvePseudoUrl(s)); EvalSettings::resolvePseudoUrl(s));
auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy); state.registerAccessor(accessor);
return state.rootPath(CanonPath(state.store->toRealPath(storePath))); return SourcePath(accessor);
} }
else if (hasPrefix(s, "flake:")) { else if (hasPrefix(s, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false);
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first; auto accessor = flakeRef.resolve(state.store).lazyFetch(state.store).first;
return state.rootPath(CanonPath(state.store->toRealPath(storePath))); return SourcePath(accessor);
} }
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {

View file

@ -7,6 +7,7 @@
namespace nix { namespace nix {
class Store; class Store;
struct SourcePath;
struct EvalSettings : Config struct EvalSettings : Config
{ {
@ -22,7 +23,7 @@ struct EvalSettings : Config
* @todo Return (`std::optional` of) `SourceAccssor` or something * @todo Return (`std::optional` of) `SourceAccssor` or something
* more structured instead of mere `std::string`? * more structured instead of mere `std::string`?
*/ */
using LookupPathHook = std::optional<std::string>(ref<Store> store, std::string_view); using LookupPathHook = std::optional<SourcePath>(ref<Store> store, std::string_view);
/** /**
* Map from "scheme" to a `LookupPathHook`. * Map from "scheme" to a `LookupPathHook`.

View file

@ -859,6 +859,11 @@ void Value::mkPath(const SourcePath & path)
mkPath(&*path.accessor, makeImmutableString(path.path.abs())); mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
} }
void EvalState::registerAccessor(const ref<SourceAccessor> accessor)
{
sourceAccessors.push_back(accessor);
}
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
{ {
@ -1973,10 +1978,21 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * const * lists, co
v.mkList(list); v.mkList(list);
} }
Value * resolveOutPath(EvalState & state, Value * v, const PosIdx pos)
{
state.forceValue(*v, pos);
if (v->type() != nAttrs)
return v;
auto found = v->attrs()->find(state.sOutPath);
if (found != v->attrs()->end())
return resolveOutPath(state, found->value, pos);
return v;
}
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{ {
NixStringContext context; NixStringContext context;
std::shared_ptr<SourceAccessor> accessor;
std::vector<BackedStringView> s; std::vector<BackedStringView> s;
size_t sSize = 0; size_t sSize = 0;
NixInt n{0}; NixInt n{0};
@ -2010,8 +2026,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
Value * vTmpP = values.data(); Value * vTmpP = values.data();
for (auto & [i_pos, i] : *es) { for (auto & [i_pos, i] : *es) {
Value & vTmp = *vTmpP++; Value & vTmp0 = *vTmpP++;
i->eval(state, env, vTmp); i->eval(state, env, vTmp0);
Value & vTmp = *resolveOutPath(state, &vTmp0, i_pos);
/* If the first element is a path, then the result will also /* If the first element is a path, then the result will also
be a path, we don't copy anything (yet - that's done later, be a path, we don't copy anything (yet - that's done later,
@ -2019,6 +2036,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
and none of the strings are allowed to have contexts. */ and none of the strings are allowed to have contexts. */
if (first) { if (first) {
firstType = vTmp.type(); firstType = vTmp.type();
if (firstType == nPath) {
accessor = vTmp.path().accessor;
}
} }
if (firstType == nInt) { if (firstType == nInt) {
@ -2065,7 +2085,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
else if (firstType == nPath) { else if (firstType == nPath) {
if (!context.empty()) if (!context.empty())
state.error<EvalError>("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow(); state.error<EvalError>("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow();
v.mkPath(state.rootPath(CanonPath(canonPath(str())))); v.mkPath({ref(accessor), CanonPath(str())});
} else } else
v.mkStringMove(c_str(), context); v.mkStringMove(c_str(), context);
} }
@ -3053,12 +3073,14 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_
if (!suffixOpt) continue; if (!suffixOpt) continue;
auto suffix = *suffixOpt; auto suffix = *suffixOpt;
auto rOpt = resolveLookupPathPath(i.path); auto rOpt = resolveLookupPathPath(
i.path,
true);
if (!rOpt) continue; if (!rOpt) continue;
auto r = *rOpt; auto r = *rOpt;
Path res = suffix == "" ? r : concatStrings(r, "/", suffix); auto res = r / suffix;
if (pathExists(res)) return rootPath(CanonPath(canonPath(res))); if (res.pathExists()) return res;
} }
if (hasPrefix(path, "nix/")) if (hasPrefix(path, "nix/"))
@ -3073,18 +3095,20 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_
} }
std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl) std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl)
{ {
auto & value = value0.s; auto & value = value0.s;
auto i = lookupPathResolved.find(value); auto i = lookupPathResolved.find(value);
if (i != lookupPathResolved.end()) return i->second; if (i != lookupPathResolved.end()) return i->second;
auto finish = [&](std::string res) { auto finish = [&](SourcePath res) {
debug("resolved search path element '%s' to '%s'", value, res); debug("resolved search path element '%s' to '%s'", value, res);
lookupPathResolved.emplace(value, res); lookupPathResolved.emplace(value, res);
return res; return res;
}; };
std::optional<SourcePath> res;
if (EvalSettings::isPseudoUrl(value)) { if (EvalSettings::isPseudoUrl(value)) {
try { try {
auto accessor = fetchers::downloadTarball( auto accessor = fetchers::downloadTarball(
@ -3092,7 +3116,7 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
fetchSettings, fetchSettings,
EvalSettings::resolvePseudoUrl(value)); EvalSettings::resolvePseudoUrl(value));
auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy); auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy);
return finish(store->toRealPath(storePath)); res.emplace(rootPath(CanonPath(store->toRealPath(storePath))));
} catch (Error & e) { } catch (Error & e) {
logWarning({ logWarning({
.msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value) .msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value)
@ -3111,22 +3135,22 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
} }
{ {
auto path = absPath(value); auto path = rootPath(value);
/* Allow access to paths in the search path. */ /* Allow access to paths in the search path. */
if (initAccessControl) { if (initAccessControl) {
allowPath(path); allowPath(path.path.abs());
if (store->isInStore(path)) { if (store->isInStore(path.path.abs())) {
try { try {
StorePathSet closure; StorePathSet closure;
store->computeFSClosure(store->toStorePath(path).first, closure); store->computeFSClosure(store->toStorePath(path.path.abs()).first, closure);
for (auto & p : closure) for (auto & p : closure)
allowPath(p); allowPath(p);
} catch (InvalidPath &) { } } catch (InvalidPath &) { }
} }
} }
if (pathExists(path)) if (path.pathExists())
return finish(std::move(path)); return finish(std::move(path));
else { else {
logWarning({ logWarning({

View file

@ -249,6 +249,9 @@ public:
const SourcePath callFlakeInternal; const SourcePath callFlakeInternal;
/* A collection of InputAccessors, just to keep them alive. */
std::list<ref<SourceAccessor>> sourceAccessors;
/** /**
* Store used to materialise .drv files. * Store used to materialise .drv files.
*/ */
@ -339,7 +342,7 @@ private:
LookupPath lookupPath; LookupPath lookupPath;
std::map<std::string, std::optional<std::string>> lookupPathResolved; std::map<std::string, std::optional<SourcePath>> lookupPathResolved;
/** /**
* Cache used by prim_match(). * Cache used by prim_match().
@ -381,6 +384,8 @@ public:
*/ */
SourcePath rootPath(PathView path); SourcePath rootPath(PathView path);
void registerAccessor(ref<SourceAccessor> accessor);
/** /**
* Allow access to a path. * Allow access to a path.
*/ */
@ -446,7 +451,7 @@ public:
* *
* If it is not found, return `std::nullopt` * If it is not found, return `std::nullopt`
*/ */
std::optional<std::string> resolveLookupPathPath( std::optional<SourcePath> resolveLookupPathPath(
const LookupPath::Path & elem, const LookupPath::Path & elem,
bool initAccessControl = false); bool initAccessControl = false);

View file

@ -45,7 +45,7 @@ void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << s; str << path;
} }
void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const

View file

@ -131,12 +131,12 @@ struct ExprString : Expr
struct ExprPath : Expr struct ExprPath : Expr
{ {
ref<SourceAccessor> accessor; const SourcePath path;
std::string s;
Value v; Value v;
ExprPath(ref<SourceAccessor> accessor, std::string s) : accessor(accessor), s(std::move(s)) ExprPath(SourcePath && path)
: path(path)
{ {
v.mkPath(&*accessor, this->s.c_str()); v.mkPath(&*path.accessor, strdup(path.path.abs().c_str()));
} }
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS COMMON_METHODS

View file

@ -136,6 +136,10 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) {
std::vector<std::pair<nix::AttrName, nix::PosIdx>> * inheritAttrs; std::vector<std::pair<nix::AttrName, nix::PosIdx>> * inheritAttrs;
std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts; std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts;
std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>> * ind_string_parts; std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>> * ind_string_parts;
struct {
nix::Expr * e;
bool appendSlash;
} pathStart;
} }
%type <e> start expr expr_function expr_if expr_op %type <e> start expr expr_function expr_if expr_op
@ -149,7 +153,8 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) {
%type <inheritAttrs> attrs %type <inheritAttrs> attrs
%type <string_parts> string_parts_interpolated %type <string_parts> string_parts_interpolated
%type <ind_string_parts> ind_string_parts %type <ind_string_parts> ind_string_parts
%type <e> path_start string_parts string_attr %type <pathStart> path_start
%type <e> string_parts string_attr
%type <id> attr %type <id> attr
%token <id> ID %token <id> ID
%token <str> STR IND_STR %token <str> STR IND_STR
@ -295,9 +300,11 @@ expr_simple
$$ = state->stripIndentation(CUR_POS, std::move(*$2)); $$ = state->stripIndentation(CUR_POS, std::move(*$2));
delete $2; delete $2;
} }
| path_start PATH_END | path_start PATH_END { $$ = $1.e; }
| path_start string_parts_interpolated PATH_END { | path_start string_parts_interpolated PATH_END {
$2->insert($2->begin(), {state->at(@1), $1}); if ($1.appendSlash)
$2->insert($2->begin(), {noPos, new ExprString("/")});
$2->insert($2->begin(), {state->at(@1), $1.e});
$$ = new ExprConcatStrings(CUR_POS, false, $2); $$ = new ExprConcatStrings(CUR_POS, false, $2);
} }
| SPATH { | SPATH {
@ -350,11 +357,17 @@ string_parts_interpolated
path_start path_start
: PATH { : PATH {
Path path(absPath({$1.p, $1.l}, state->basePath.path.abs())); std::string_view path({$1.p, $1.l});
/* add back in the trailing '/' to the first segment */ $$ = {
if ($1.p[$1.l-1] == '/' && $1.l > 1) .e = new ExprPath(
path += "/"; /* Absolute paths are always interpreted relative to the
$$ = new ExprPath(ref<SourceAccessor>(state->rootFS), std::move(path)); root filesystem accessor, rather than the accessor of the
current Nix expression. */
hasPrefix(path, "/")
? SourcePath{state->rootFS, CanonPath(path)}
: SourcePath{state->basePath.accessor, CanonPath(path, state->basePath.path)}),
.appendSlash = hasSuffix(path, "/")
};
} }
| HPATH { | HPATH {
if (state->settings.pureEval) { if (state->settings.pureEval) {
@ -363,8 +376,8 @@ path_start
std::string_view($1.p, $1.l) std::string_view($1.p, $1.l)
); );
} }
Path path(getHome() + std::string($1.p + 1, $1.l - 1)); CanonPath path(getHome() + std::string($1.p + 1, $1.l - 1));
$$ = new ExprPath(ref<SourceAccessor>(state->rootFS), std::move(path)); $$ = {.e = new ExprPath(SourcePath{state->rootFS, std::move(path)}), .appendSlash = true};
} }
; ;

View file

@ -178,6 +178,9 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
auto path = realisePath(state, pos, vPath, std::nullopt); auto path = realisePath(state, pos, vPath, std::nullopt);
auto path2 = path.path.abs(); auto path2 = path.path.abs();
// FIXME: corruption?
// TODO(tomberek): re-enable this code?
#if 0
// FIXME // FIXME
auto isValidDerivationInStore = [&]() -> std::optional<StorePath> { auto isValidDerivationInStore = [&]() -> std::optional<StorePath> {
if (!state.store->isStorePath(path2)) if (!state.store->isStorePath(path2))
@ -218,7 +221,9 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
state.forceAttrs(v, pos, "while calling imported-drv-to-derivation.nix.gen.hh"); state.forceAttrs(v, pos, "while calling imported-drv-to-derivation.nix.gen.hh");
} }
else { else
#endif
{
if (!vScope) if (!vScope)
state.evalFile(path, v); state.evalFile(path, v);
else { else {

View file

@ -19,21 +19,20 @@ namespace nix {
void emitTreeAttrs( void emitTreeAttrs(
EvalState & state, EvalState & state,
const StorePath & storePath,
const fetchers::Input & input, const fetchers::Input & input,
Value & v, Value & v,
std::function<void(Value &)> setOutPath,
bool emptyRevFallback, bool emptyRevFallback,
bool forceDirty) bool forceDirty)
{ {
auto attrs = state.buildBindings(100); auto attrs = state.buildBindings(100);
state.mkStorePathString(storePath, attrs.alloc(state.sOutPath)); setOutPath(attrs.alloc(state.sOutPath));
// FIXME: support arbitrary input attributes. // FIXME: support arbitrary input attributes.
auto narHash = input.getNarHash(); if (auto narHash = input.getNarHash())
assert(narHash); attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
if (input.getType() == "git") if (input.getType() == "git")
attrs.alloc("submodules").mkBool( attrs.alloc("submodules").mkBool(
@ -72,10 +71,28 @@ void emitTreeAttrs(
v.mkAttrs(attrs); v.mkAttrs(attrs);
} }
void emitTreeAttrs(
EvalState & state,
const SourcePath & storePath,
const fetchers::Input & input,
Value & v,
bool emptyRevFallback,
bool forceDirty)
{
emitTreeAttrs(state, input, v,
[&](Value & vOutPath) {
state.registerAccessor(storePath.accessor);
vOutPath.mkPath(storePath);
},
emptyRevFallback,
forceDirty);
}
struct FetchTreeParams { struct FetchTreeParams {
bool emptyRevFallback = false; bool emptyRevFallback = false;
bool allowNameArgument = false; bool allowNameArgument = false;
bool isFetchGit = false; bool isFetchGit = false;
bool returnPath = true; // whether to return a SourcePath or a StorePath
}; };
static void fetchTree( static void fetchTree(
@ -112,7 +129,9 @@ static void fetchTree(
for (auto & attr : *args[0]->attrs()) { for (auto & attr : *args[0]->attrs()) {
if (attr.name == state.sType) continue; if (attr.name == state.sType) continue;
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) { if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned(); auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned();
attrs.emplace(state.symbols[attr.name], attrs.emplace(state.symbols[attr.name],
@ -197,7 +216,14 @@ static void fetchTree(
state.allowPath(storePath); state.allowPath(storePath);
emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false); emitTreeAttrs(state, input2, v,
[&](Value & vOutPath) {
if (params.returnPath)
vOutPath.mkPath(state.rootPath(state.store->toRealPath(storePath)));
else
state.mkStorePathString(storePath, vOutPath);
},
params.emptyRevFallback, false);
} }
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
@ -614,7 +640,8 @@ static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, V
FetchTreeParams { FetchTreeParams {
.emptyRevFallback = true, .emptyRevFallback = true,
.allowNameArgument = true, .allowNameArgument = true,
.isFetchGit = true .isFetchGit = true,
.returnPath = false,
}); });
} }

View file

@ -167,6 +167,8 @@ bool Input::contains(const Input & other) const
std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
{ {
// TODO: lazy-trees gets rid of this. Why?
#if 0
if (!scheme) if (!scheme)
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
@ -187,6 +189,7 @@ std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
debug("substitution of input '%s' failed: %s", to_string(), e.what()); debug("substitution of input '%s' failed: %s", to_string(), e.what());
} }
} }
#endif
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> { auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
try { try {
@ -194,8 +197,9 @@ std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, final.getName()); auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, final.getName());
auto narHash = store->queryPathInfo(storePath)->narHash; // TODO: do we really want to throw this out?
final.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); // auto narHash = store->queryPathInfo(storePath)->narHash;
// final.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
scheme->checkLocks(*this, final); scheme->checkLocks(*this, final);

View file

@ -19,7 +19,7 @@ using namespace flake;
namespace flake { namespace flake {
typedef std::pair<StorePath, FlakeRef> FetchedFlake; typedef std::pair<ref<SourceAccessor>, FlakeRef> FetchedFlake;
typedef std::vector<std::pair<FlakeRef, FetchedFlake>> FlakeCache; typedef std::vector<std::pair<FlakeRef, FetchedFlake>> FlakeCache;
static std::optional<FetchedFlake> lookupInFlakeCache( static std::optional<FetchedFlake> lookupInFlakeCache(
@ -38,43 +38,17 @@ static std::optional<FetchedFlake> lookupInFlakeCache(
return std::nullopt; return std::nullopt;
} }
static std::tuple<StorePath, FlakeRef, FlakeRef> fetchOrSubstituteTree( static FlakeRef maybeResolve(
EvalState & state, EvalState & state,
const FlakeRef & originalRef, const FlakeRef & originalRef,
bool allowLookup, bool useRegistries)
FlakeCache & flakeCache)
{ {
auto fetched = lookupInFlakeCache(flakeCache, originalRef);
FlakeRef resolvedRef = originalRef;
if (!fetched) {
if (originalRef.input.isDirect()) { if (originalRef.input.isDirect()) {
fetched.emplace(originalRef.fetchTree(state.store)); if (!useRegistries)
} else {
if (allowLookup) {
resolvedRef = originalRef.resolve(state.store);
auto fetchedResolved = lookupInFlakeCache(flakeCache, originalRef);
if (!fetchedResolved) fetchedResolved.emplace(resolvedRef.fetchTree(state.store));
flakeCache.push_back({resolvedRef, *fetchedResolved});
fetched.emplace(*fetchedResolved);
}
else {
throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", originalRef); throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", originalRef);
} return originalRef.resolve(state.store);
} } else
flakeCache.push_back({originalRef, *fetched}); return originalRef;
}
auto [storePath, lockedRef] = *fetched;
debug("got tree '%s' from '%s'",
state.store->printStorePath(storePath), lockedRef);
state.allowPath(storePath);
assert(!originalRef.input.getNarHash() || storePath == originalRef.input.computeStorePath(*state.store));
return {std::move(storePath), resolvedRef, lockedRef};
} }
static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos) static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
@ -319,10 +293,12 @@ static Flake getFlake(
FlakeCache & flakeCache, FlakeCache & flakeCache,
InputPath lockRootPath) InputPath lockRootPath)
{ {
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree( auto resolvedRef = maybeResolve(state, originalRef, allowLookup);
state, originalRef, allowLookup, flakeCache); auto [accessor, lockedRef] = resolvedRef.lazyFetch(state.store);
return readFlake(state, originalRef, resolvedRef, lockedRef, state.rootPath(state.store->toRealPath(storePath)), lockRootPath); state.registerAccessor(accessor);
return readFlake(state, originalRef, resolvedRef, lockedRef, SourcePath {accessor, CanonPath::root}, lockRootPath);
} }
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache) Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
@ -615,12 +591,17 @@ LockedFlake lockFlake(
} }
else { else {
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree( auto [path, lockedRef] = [&]() -> std::tuple<SourcePath, FlakeRef>
state, *input.ref, useRegistries, flakeCache); {
auto resolvedRef = maybeResolve(state, *input.ref, useRegistries);
auto [accessor, lockedRef] = resolvedRef.lazyFetch(state.store);
state.registerAccessor(accessor);
return {SourcePath(accessor), lockedRef};
}();
auto childNode = make_ref<LockedNode>(lockedRef, ref, false); auto childNode = make_ref<LockedNode>(lockedRef, ref, false);
nodePaths.emplace(childNode, state.rootPath(state.store->toRealPath(storePath))); nodePaths.emplace(childNode, path);
node->inputs.insert_or_assign(id, childNode); node->inputs.insert_or_assign(id, childNode);
} }
@ -768,21 +749,23 @@ void callFlake(EvalState & state,
auto lockedNode = node.dynamic_pointer_cast<const LockedNode>(); auto lockedNode = node.dynamic_pointer_cast<const LockedNode>();
/*
// FIXME: This is a hack to support chroot stores. Remove this // FIXME: This is a hack to support chroot stores. Remove this
// once we can pass a sourcePath rather than a storePath to // once we can pass a sourcePath rather than a storePath to
// call-flake.nix. // call-flake.nix.
auto path = sourcePath.path.abs(); // auto path = sourcePath.path.abs();
if (auto store = state.store.dynamic_pointer_cast<LocalFSStore>()) { // if (auto store = state.store.dynamic_pointer_cast<LocalFSStore>()) {
auto realStoreDir = store->getRealStoreDir(); // auto realStoreDir = store->getRealStoreDir();
if (isInDir(path, realStoreDir)) // if (isInDir(path, realStoreDir))
path = store->storeDir + path.substr(realStoreDir.size()); // path = store->storeDir + path.substr(realStoreDir.size());
} // }
//
auto [storePath, subdir] = state.store->toStorePath(path); // auto [storePath, subdir] = state.store->toStorePath(path);
*/
emitTreeAttrs( emitTreeAttrs(
state, state,
storePath, SourcePath(sourcePath.accessor),
lockedNode ? lockedNode->lockedRef.input : lockedFlake.flake.lockedRef.input, lockedNode ? lockedNode->lockedRef.input : lockedFlake.flake.lockedRef.input,
vSourceInfo, vSourceInfo,
false, false,
@ -793,7 +776,7 @@ void callFlake(EvalState & state,
override override
.alloc(state.symbols.create("dir")) .alloc(state.symbols.create("dir"))
.mkString(CanonPath(subdir).rel()); .mkString(sourcePath.path.rel());
overrides.alloc(state.symbols.create(key->second)).mkAttrs(override); overrides.alloc(state.symbols.create(key->second)).mkAttrs(override);
} }

View file

@ -218,7 +218,7 @@ void callFlake(
void emitTreeAttrs( void emitTreeAttrs(
EvalState & state, EvalState & state,
const StorePath & storePath, const SourcePath & storePath,
const fetchers::Input & input, const fetchers::Input & input,
Value & v, Value & v,
bool emptyRevFallback = false, bool emptyRevFallback = false,

View file

@ -287,10 +287,10 @@ FlakeRef FlakeRef::fromAttrs(
fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); fetchers::maybeGetStrAttr(attrs, "dir").value_or(""));
} }
std::pair<StorePath, FlakeRef> FlakeRef::fetchTree(ref<Store> store) const std::pair<ref<SourceAccessor>, FlakeRef> FlakeRef::lazyFetch(ref<Store> store) const
{ {
auto [storePath, lockedInput] = input.fetchToStore(store); auto [accessor, lockedInput] = input.getAccessor(store);
return {std::move(storePath), FlakeRef(std::move(lockedInput), subdir)}; return {accessor, FlakeRef(std::move(lockedInput), subdir)};
} }
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec( std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(

View file

@ -63,7 +63,7 @@ struct FlakeRef
const fetchers::Settings & fetchSettings, const fetchers::Settings & fetchSettings,
const fetchers::Attrs & attrs); const fetchers::Attrs & attrs);
std::pair<StorePath, FlakeRef> fetchTree(ref<Store> store) const; std::pair<ref<SourceAccessor>, FlakeRef> lazyFetch(ref<Store> store) const;
}; };
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);

View file

@ -213,9 +213,6 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
auto lockedFlake = lockFlake(); auto lockedFlake = lockFlake();
auto & flake = lockedFlake.flake; auto & flake = lockedFlake.flake;
// Currently, all flakes are in the Nix store via the rootFS accessor.
auto storePath = store->printStorePath(store->toStorePath(flake.path.path.abs()).first);
if (json) { if (json) {
nlohmann::json j; nlohmann::json j;
if (flake.description) if (flake.description)
@ -236,7 +233,6 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
j["revCount"] = *revCount; j["revCount"] = *revCount;
if (auto lastModified = flake.lockedRef.input.getLastModified()) if (auto lastModified = flake.lockedRef.input.getLastModified())
j["lastModified"] = *lastModified; j["lastModified"] = *lastModified;
j["path"] = storePath;
j["locks"] = lockedFlake.lockFile.toJSON().first; j["locks"] = lockedFlake.lockFile.toJSON().first;
if (auto fingerprint = lockedFlake.getFingerprint(store)) if (auto fingerprint = lockedFlake.getFingerprint(store))
j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false); j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false);
@ -253,9 +249,6 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
logger->cout( logger->cout(
ANSI_BOLD "Description:" ANSI_NORMAL " %s", ANSI_BOLD "Description:" ANSI_NORMAL " %s",
*flake.description); *flake.description);
logger->cout(
ANSI_BOLD "Path:" ANSI_NORMAL " %s",
storePath);
if (auto rev = flake.lockedRef.input.getRev()) if (auto rev = flake.lockedRef.input.getRev())
logger->cout( logger->cout(
ANSI_BOLD "Revision:" ANSI_NORMAL " %s", ANSI_BOLD "Revision:" ANSI_NORMAL " %s",
@ -1523,21 +1516,15 @@ struct CmdFlakePrefetch : FlakeCommand, MixJSON
{ {
auto originalRef = getFlakeRef(); auto originalRef = getFlakeRef();
auto resolvedRef = originalRef.resolve(store); auto resolvedRef = originalRef.resolve(store);
auto [storePath, lockedRef] = resolvedRef.fetchTree(store); auto [accessor, lockedRef] = resolvedRef.lazyFetch(store);
auto hash = store->queryPathInfo(storePath)->narHash;
if (json) { if (json) {
auto res = nlohmann::json::object(); auto res = nlohmann::json::object();
res["storePath"] = store->printStorePath(storePath);
res["hash"] = hash.to_string(HashFormat::SRI, true);
res["original"] = fetchers::attrsToJSON(resolvedRef.toAttrs()); res["original"] = fetchers::attrsToJSON(resolvedRef.toAttrs());
res["locked"] = fetchers::attrsToJSON(lockedRef.toAttrs()); res["locked"] = fetchers::attrsToJSON(lockedRef.toAttrs());
logger->cout(res.dump()); logger->cout(res.dump());
} else { } else {
notice("Downloaded '%s' to '%s' (hash '%s').", notice("Fetched '%s'.", lockedRef.to_string());
lockedRef.to_string(),
store->printStorePath(storePath),
hash.to_string(HashFormat::SRI, true));
} }
} }
}; };

View file

@ -192,7 +192,6 @@ nix flake metadata "$flake1Dir" | grepQuiet 'URL:.*flake1.*'
# Test 'nix flake metadata --json'. # Test 'nix flake metadata --json'.
json=$(nix flake metadata flake1 --json | jq .) json=$(nix flake metadata flake1 --json | jq .)
[[ $(echo "$json" | jq -r .description) = 'Bla bla' ]] [[ $(echo "$json" | jq -r .description) = 'Bla bla' ]]
[[ -d $(echo "$json" | jq -r .path) ]]
[[ $(echo "$json" | jq -r .lastModified) = $(git -C "$flake1Dir" log -n1 --format=%ct) ]] [[ $(echo "$json" | jq -r .lastModified) = $(git -C "$flake1Dir" log -n1 --format=%ct) ]]
hash1=$(echo "$json" | jq -r .revision) hash1=$(echo "$json" | jq -r .revision)
[[ -n $(echo "$json" | jq -r .fingerprint) ]] [[ -n $(echo "$json" | jq -r .fingerprint) ]]