From 9c01a741b3975255c8293dc2905a73e776dd2485 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 Mar 2024 15:59:07 +0100 Subject: [PATCH] fetchTree: Return a path instead of a store path rebase of https://github.com/NixOS/nix/pull/10252 --- src/libcmd/common-eval-args.cc | 12 +++--- src/libexpr/eval-settings.hh | 3 +- src/libexpr/eval.cc | 54 ++++++++++++++++++------- src/libexpr/eval.hh | 9 ++++- src/libexpr/nixexpr.cc | 2 +- src/libexpr/nixexpr.hh | 8 ++-- src/libexpr/parser.y | 33 +++++++++++----- src/libexpr/primops.cc | 7 +++- src/libexpr/primops/fetchTree.cc | 43 ++++++++++++++++---- src/libfetchers/fetchers.cc | 8 +++- src/libflake/flake/flake.cc | 65 ++++++++++++------------------- src/libflake/flake/flake.hh | 2 +- src/libflake/flake/flakeref.cc | 6 +-- src/libflake/flake/flakeref.hh | 2 +- src/nix/flake.cc | 17 +------- tests/functional/flakes/flakes.sh | 1 - 16 files changed, 160 insertions(+), 112 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index ae9994a05f67..b520ba529d71 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -32,8 +32,8 @@ EvalSettings evalSettings { // FIXME `parseFlakeRef` should take a `std::string_view`. auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false); debug("fetching flake search path element '%s''", rest); - auto storePath = flakeRef.resolve(store).fetchTree(store).first; - return store->toRealPath(storePath); + auto storePath = flakeRef.resolve(store).lazyFetch(store).first; + return storePath; }, }, }, @@ -174,15 +174,15 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s)); - auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy); - return state.rootPath(CanonPath(state.store->toRealPath(storePath))); + state.registerAccessor(accessor); + return SourcePath(accessor); } else if (hasPrefix(s, "flake:")) { experimentalFeatureSettings.require(Xp::Flakes); auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false); - auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first; - return state.rootPath(CanonPath(state.store->toRealPath(storePath))); + auto accessor = flakeRef.resolve(state.store).lazyFetch(state.store).first; + return SourcePath(accessor); } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 3d412bbbf57f..855f31af5f52 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -7,6 +7,7 @@ namespace nix { class Store; +struct SourcePath; struct EvalSettings : Config { @@ -22,7 +23,7 @@ struct EvalSettings : Config * @todo Return (`std::optional` of) `SourceAccssor` or something * more structured instead of mere `std::string`? */ - using LookupPathHook = std::optional(ref store, std::string_view); + using LookupPathHook = std::optional(ref store, std::string_view); /** * Map from "scheme" to a `LookupPathHook`. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 92320b554e84..c1503c264ccb 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -859,6 +859,11 @@ void Value::mkPath(const SourcePath & path) mkPath(&*path.accessor, makeImmutableString(path.path.abs())); } +void EvalState::registerAccessor(const ref accessor) +{ + sourceAccessors.push_back(accessor); +} + 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); } +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) { NixStringContext context; + std::shared_ptr accessor; std::vector s; size_t sSize = 0; NixInt n{0}; @@ -2010,8 +2026,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) Value * vTmpP = values.data(); for (auto & [i_pos, i] : *es) { - Value & vTmp = *vTmpP++; - i->eval(state, env, vTmp); + Value & vTmp0 = *vTmpP++; + i->eval(state, env, vTmp0); + Value & vTmp = *resolveOutPath(state, &vTmp0, i_pos); /* 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, @@ -2019,6 +2036,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) and none of the strings are allowed to have contexts. */ if (first) { firstType = vTmp.type(); + if (firstType == nPath) { + accessor = vTmp.path().accessor; + } } if (firstType == nInt) { @@ -2065,7 +2085,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) else if (firstType == nPath) { if (!context.empty()) state.error("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 v.mkStringMove(c_str(), context); } @@ -3053,12 +3073,14 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_ if (!suffixOpt) continue; auto suffix = *suffixOpt; - auto rOpt = resolveLookupPathPath(i.path); + auto rOpt = resolveLookupPathPath( + i.path + , true); if (!rOpt) continue; auto r = *rOpt; - Path res = suffix == "" ? r : concatStrings(r, "/", suffix); - if (pathExists(res)) return rootPath(CanonPath(canonPath(res))); + auto res = r / suffix; + if (res.pathExists()) return res; } if (hasPrefix(path, "nix/")) @@ -3073,18 +3095,20 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_ } -std::optional EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl) +std::optional EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl) { auto & value = value0.s; auto i = lookupPathResolved.find(value); 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); lookupPathResolved.emplace(value, res); return res; }; + std::optional res; + if (EvalSettings::isPseudoUrl(value)) { try { auto accessor = fetchers::downloadTarball( @@ -3092,7 +3116,7 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa fetchSettings, EvalSettings::resolvePseudoUrl(value)); auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy); - return finish(store->toRealPath(storePath)); + res.emplace(rootPath(CanonPath(store->toRealPath(storePath)))); } catch (Error & e) { logWarning({ .msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value) @@ -3110,23 +3134,23 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa } } - { - auto path = absPath(value); + else { + auto path = rootPath(value); /* Allow access to paths in the search path. */ if (initAccessControl) { - allowPath(path); - if (store->isInStore(path)) { + allowPath(path.path.abs()); + if (store->isInStore(path.path.abs())) { try { StorePathSet closure; - store->computeFSClosure(store->toStorePath(path).first, closure); + store->computeFSClosure(store->toStorePath(path.path.abs()).first, closure); for (auto & p : closure) allowPath(p); } catch (InvalidPath &) { } } } - if (pathExists(path)) + if (path.pathExists()) return finish(std::move(path)); else { logWarning({ diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ddf5dcf94783..e3ef0526964e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -249,6 +249,9 @@ public: const SourcePath callFlakeInternal; + /* A collection of InputAccessors, just to keep them alive. */ + std::list> sourceAccessors; + /** * Store used to materialise .drv files. */ @@ -339,7 +342,7 @@ private: LookupPath lookupPath; - std::map> lookupPathResolved; + std::map> lookupPathResolved; /** * Cache used by prim_match(). @@ -381,6 +384,8 @@ public: */ SourcePath rootPath(PathView path); + void registerAccessor(ref accessor); + /** * Allow access to a path. */ @@ -446,7 +451,7 @@ public: * * If it is not found, return `std::nullopt` */ - std::optional resolveLookupPathPath( + std::optional resolveLookupPathPath( const LookupPath::Path & elem, bool initAccessControl = false); diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index dbc74faf9a78..31c7d1514a65 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -45,7 +45,7 @@ void ExprString::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 diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 7868834f1955..4bfed40db62f 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -131,12 +131,12 @@ struct ExprString : Expr struct ExprPath : Expr { - ref accessor; - std::string s; + const SourcePath path; Value v; - ExprPath(ref 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; COMMON_METHODS diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index f2ccca7fcdd2..cb9e70a61466 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -136,6 +136,10 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) { std::vector> * inheritAttrs; std::vector> * string_parts; std::vector>> * ind_string_parts; + struct { + nix::Expr * e; + bool appendSlash; + } pathStart; } %type start expr expr_function expr_if expr_op @@ -149,7 +153,8 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) { %type attrs %type string_parts_interpolated %type ind_string_parts -%type path_start string_parts string_attr +%type path_start +%type string_parts string_attr %type attr %token ID %token STR IND_STR @@ -295,9 +300,11 @@ expr_simple $$ = state->stripIndentation(CUR_POS, std::move(*$2)); delete $2; } - | path_start PATH_END + | path_start PATH_END { $$ = $1.e; } | 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); } | SPATH { @@ -350,11 +357,17 @@ string_parts_interpolated path_start : PATH { - Path path(absPath({$1.p, $1.l}, state->basePath.path.abs())); - /* add back in the trailing '/' to the first segment */ - if ($1.p[$1.l-1] == '/' && $1.l > 1) - path += "/"; - $$ = new ExprPath(ref(state->rootFS), std::move(path)); + std::string_view path({$1.p, $1.l}); + $$ = { + .e = new ExprPath( + /* Absolute paths are always interpreted relative to the + 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 { if (state->settings.pureEval) { @@ -363,8 +376,8 @@ path_start std::string_view($1.p, $1.l) ); } - Path path(getHome() + std::string($1.p + 1, $1.l - 1)); - $$ = new ExprPath(ref(state->rootFS), std::move(path)); + CanonPath path(getHome() + std::string($1.p + 1, $1.l - 1)); + $$ = {.e = new ExprPath(SourcePath{state->rootFS, std::move(path)}), .appendSlash = true}; } ; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9de8ff599eb6..e4386be0b253 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -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 path2 = path.path.abs(); +// FIXME: corruption? +// TODO(tomberek): re-enable this code? +#if 0 // FIXME auto isValidDerivationInStore = [&]() -> std::optional { 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"); } - else { + else +#endif + { if (!vScope) state.evalFile(path, v); else { diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 5d074e62303d..ed5eb1be7804 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -17,23 +17,22 @@ namespace nix { -void emitTreeAttrs( +static void emitTreeAttrs( EvalState & state, - const StorePath & storePath, const fetchers::Input & input, Value & v, + std::function setOutPath, bool emptyRevFallback, bool forceDirty) { auto attrs = state.buildBindings(100); - state.mkStorePathString(storePath, attrs.alloc(state.sOutPath)); + setOutPath(attrs.alloc(state.sOutPath)); // FIXME: support arbitrary input attributes. - auto narHash = input.getNarHash(); - assert(narHash); - attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true)); + if (auto narHash = input.getNarHash()) + attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true)); if (input.getType() == "git") attrs.alloc("submodules").mkBool( @@ -72,10 +71,28 @@ void emitTreeAttrs( 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 { bool emptyRevFallback = false; bool allowNameArgument = false; bool isFetchGit = false; + bool returnPath = true; // whether to return a SourcePath or a StorePath }; static void fetchTree( @@ -112,7 +129,9 @@ static void fetchTree( for (auto & attr : *args[0]->attrs()) { if (attr.name == state.sType) continue; + state.forceValue(*attr.value, attr.pos); + if (attr.value->type() == nPath || attr.value->type() == nString) { auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned(); attrs.emplace(state.symbols[attr.name], @@ -197,7 +216,14 @@ static void fetchTree( 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) @@ -614,7 +640,8 @@ static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, V FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true, - .isFetchGit = true + .isFetchGit = true, + .returnPath = false, }); } diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index dee1f687b2ea..cae270b2dc39 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -167,6 +167,8 @@ bool Input::contains(const Input & other) const std::pair Input::fetchToStore(ref store) const { +// TODO: lazy-trees gets rid of this. Why? +#if 0 if (!scheme) throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); @@ -187,6 +189,7 @@ std::pair Input::fetchToStore(ref store) const debug("substitution of input '%s' failed: %s", to_string(), e.what()); } } +#endif auto [storePath, input] = [&]() -> std::pair { try { @@ -194,8 +197,9 @@ std::pair Input::fetchToStore(ref store) const auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, final.getName()); - auto narHash = store->queryPathInfo(storePath)->narHash; - final.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); + // TODO: do we really want to throw this out? + // auto narHash = store->queryPathInfo(storePath)->narHash; + // final.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); scheme->checkLocks(*this, final); diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index fd1183514c9f..828ba1b0e481 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -19,7 +19,7 @@ using namespace flake; namespace flake { -typedef std::pair FetchedFlake; +typedef std::pair, FlakeRef> FetchedFlake; typedef std::vector> FlakeCache; static std::optional lookupInFlakeCache( @@ -38,43 +38,17 @@ static std::optional lookupInFlakeCache( return std::nullopt; } -static std::tuple fetchOrSubstituteTree( +static FlakeRef maybeResolve( EvalState & state, const FlakeRef & originalRef, - bool allowLookup, - FlakeCache & flakeCache) + bool useRegistries) { - auto fetched = lookupInFlakeCache(flakeCache, originalRef); - FlakeRef resolvedRef = originalRef; - - if (!fetched) { if (originalRef.input.isDirect()) { - fetched.emplace(originalRef.fetchTree(state.store)); - } 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 { + if (!useRegistries) throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", originalRef); - } - } - flakeCache.push_back({originalRef, *fetched}); - } - - 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}; + return originalRef.resolve(state.store); + } else + return originalRef; } static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos) @@ -319,10 +293,12 @@ static Flake getFlake( FlakeCache & flakeCache, InputPath lockRootPath) { - auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, originalRef, allowLookup, flakeCache); + auto resolvedRef = maybeResolve(state, originalRef, allowLookup); + auto [accessor, lockedRef] = resolvedRef.lazyFetch(state.store); + + state.registerAccessor(accessor); - return readFlake(state, originalRef, resolvedRef, lockedRef, state.rootPath(state.store->toRealPath(storePath)), lockRootPath); + return readFlake(state, originalRef, resolvedRef, lockedRef, SourcePath {accessor, CanonPath::root}, lockRootPath); } Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache) @@ -615,12 +591,17 @@ LockedFlake lockFlake( } else { - auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, *input.ref, useRegistries, flakeCache); + auto [path, lockedRef] = [&]() -> std::tuple + { + 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(lockedRef, ref, false); - nodePaths.emplace(childNode, state.rootPath(state.store->toRealPath(storePath))); + nodePaths.emplace(childNode, path); node->inputs.insert_or_assign(id, childNode); } @@ -768,6 +749,7 @@ void callFlake(EvalState & state, auto lockedNode = node.dynamic_pointer_cast(); + /* // FIXME: This is a hack to support chroot stores. Remove this // once we can pass a sourcePath rather than a storePath to // call-flake.nix. @@ -779,10 +761,11 @@ void callFlake(EvalState & state, } auto [storePath, subdir] = state.store->toStorePath(path); + */ emitTreeAttrs( state, - storePath, + SourcePath(sourcePath.accessor), lockedNode ? lockedNode->lockedRef.input : lockedFlake.flake.lockedRef.input, vSourceInfo, false, @@ -793,7 +776,7 @@ void callFlake(EvalState & state, override .alloc(state.symbols.create("dir")) - .mkString(CanonPath(subdir).rel()); + .mkString(sourcePath.path.rel()); overrides.alloc(state.symbols.create(key->second)).mkAttrs(override); } diff --git a/src/libflake/flake/flake.hh b/src/libflake/flake/flake.hh index cce17009ce35..4d9482336f5f 100644 --- a/src/libflake/flake/flake.hh +++ b/src/libflake/flake/flake.hh @@ -218,7 +218,7 @@ void callFlake( void emitTreeAttrs( EvalState & state, - const StorePath & storePath, + const SourcePath & storePath, const fetchers::Input & input, Value & v, bool emptyRevFallback = false, diff --git a/src/libflake/flake/flakeref.cc b/src/libflake/flake/flakeref.cc index a57fce9f319a..20e4de0dfc61 100644 --- a/src/libflake/flake/flakeref.cc +++ b/src/libflake/flake/flakeref.cc @@ -287,10 +287,10 @@ FlakeRef FlakeRef::fromAttrs( fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); } -std::pair FlakeRef::fetchTree(ref store) const +std::pair, FlakeRef> FlakeRef::lazyFetch(ref store) const { - auto [storePath, lockedInput] = input.fetchToStore(store); - return {std::move(storePath), FlakeRef(std::move(lockedInput), subdir)}; + auto [accessor, lockedInput] = input.getAccessor(store); + return {accessor, FlakeRef(std::move(lockedInput), subdir)}; } std::tuple parseFlakeRefWithFragmentAndExtendedOutputsSpec( diff --git a/src/libflake/flake/flakeref.hh b/src/libflake/flake/flakeref.hh index 1064538a72a2..551f36da1066 100644 --- a/src/libflake/flake/flakeref.hh +++ b/src/libflake/flake/flakeref.hh @@ -63,7 +63,7 @@ struct FlakeRef const fetchers::Settings & fetchSettings, const fetchers::Attrs & attrs); - std::pair fetchTree(ref store) const; + std::pair, FlakeRef> lazyFetch(ref store) const; }; std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b7bbb767b31a..928f7c9fd819 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -213,9 +213,6 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON auto lockedFlake = lockFlake(); 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) { nlohmann::json j; if (flake.description) @@ -236,7 +233,6 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON j["revCount"] = *revCount; if (auto lastModified = flake.lockedRef.input.getLastModified()) j["lastModified"] = *lastModified; - j["path"] = storePath; j["locks"] = lockedFlake.lockFile.toJSON().first; if (auto fingerprint = lockedFlake.getFingerprint(store)) j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false); @@ -253,9 +249,6 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON logger->cout( ANSI_BOLD "Description:" ANSI_NORMAL " %s", *flake.description); - logger->cout( - ANSI_BOLD "Path:" ANSI_NORMAL " %s", - storePath); if (auto rev = flake.lockedRef.input.getRev()) logger->cout( ANSI_BOLD "Revision:" ANSI_NORMAL " %s", @@ -1523,21 +1516,15 @@ struct CmdFlakePrefetch : FlakeCommand, MixJSON { auto originalRef = getFlakeRef(); auto resolvedRef = originalRef.resolve(store); - auto [storePath, lockedRef] = resolvedRef.fetchTree(store); - auto hash = store->queryPathInfo(storePath)->narHash; + auto [accessor, lockedRef] = resolvedRef.lazyFetch(store); if (json) { 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["locked"] = fetchers::attrsToJSON(lockedRef.toAttrs()); logger->cout(res.dump()); } else { - notice("Downloaded '%s' to '%s' (hash '%s').", - lockedRef.to_string(), - store->printStorePath(storePath), - hash.to_string(HashFormat::SRI, true)); + notice("Fetched '%s'.", lockedRef.to_string()); } } }; diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 26b91eda751a..58889ecf127f 100755 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -192,7 +192,6 @@ nix flake metadata "$flake1Dir" | grepQuiet 'URL:.*flake1.*' # Test 'nix flake metadata --json'. json=$(nix flake metadata flake1 --json | jq .) [[ $(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) ]] hash1=$(echo "$json" | jq -r .revision) [[ -n $(echo "$json" | jq -r .fingerprint) ]]