diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 3254ad0bc12..ae0d50c2554 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2360,6 +2360,23 @@ static RegisterPrimOp primop_hashFile({ .fun = prim_hashFile, }); +static RegisterPrimOp primop_narHash({ + .name = "__narHash", + .args = {"p"}, + .doc = R"( + Return an SRI representation of the SHA-256 hash of the NAR serialisation of the path *p*. + )", + .fun = + [](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + auto path = state.realisePath(pos, *args[0]); + auto hash = + fetchToStore2(state.fetchSettings, *state.store, path.resolveSymlinks(), FetchMode::DryRun).second; + v.mkString(hash.to_string(HashFormat::SRI, true), state.mem); + }, + // FIXME: may be useful to expose to the user. + .internal = true, +}); + static const Value & fileTypeToString(EvalState & state, SourceAccessor::Type type) { struct Constants diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index dda81b9d328..691d13404f6 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -30,12 +30,16 @@ void emitTreeAttrs( { auto attrs = state.buildBindings(100); - state.mkStorePathString(storePath, attrs.alloc(state.s.outPath)); + auto & vStorePath = attrs.alloc(state.s.outPath); + state.mkStorePathString(storePath, vStorePath); // FIXME: support arbitrary input attributes. if (auto narHash = input.getNarHash()) attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true), state.mem); + else + // Lazily compute the NAR hash for backward compatibility. + attrs.alloc("narHash").mkApp(*get(state.internalPrimOps, "narHash"), &vStorePath); if (input.getType() == "git") attrs.alloc("submodules").mkBool(fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false)); diff --git a/src/libflake/flake-primops.cc b/src/libflake/flake-primops.cc index 3bbe232b0a1..754a2f72b60 100644 --- a/src/libflake/flake-primops.cc +++ b/src/libflake/flake-primops.cc @@ -35,8 +35,16 @@ namespace nix::flake::primops { PrimOp getFlake(const Settings & settings) { auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + NixStringContext context; std::string flakeRefS( - state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); + state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getFlake")); + auto rewrites = state.realiseContext(context); + flakeRefS = state.devirtualize(rewriteStrings(flakeRefS, rewrites), context); + if (hasContext(context)) + // FIXME: this should really be an error. + warn( + "In 'builtins.getFlake', the flakeref '%s' has string context, but that's not allowed. This may become a fatal error in the future.", + flakeRefS); auto flakeRef = nix::parseFlakeRef(state.fetchSettings, flakeRefS, {}, true); if (state.settings.pureEval && !flakeRef.input.isLocked(state.fetchSettings)) throw Error( @@ -125,7 +133,9 @@ static void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value ** { state.forceAttrs(*args[0], noPos, "while evaluating the argument passed to builtins.flakeRefToString"); fetchers::Attrs attrs; + NixStringContext context; for (const auto & attr : *args[0]->attrs()) { + state.forceValue(*attr.value, attr.pos); auto t = attr.value->type(); if (t == nInt) { auto intValue = attr.value->integer().value; @@ -142,7 +152,9 @@ static void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value ** } else if (t == nBool) { attrs.emplace(state.symbols[attr.name], Explicit{attr.value->boolean()}); } else if (t == nString) { - attrs.emplace(state.symbols[attr.name], std::string(attr.value->string_view())); + auto s = state.forceString( + *attr.value, context, attr.pos, "while evaluating an attribute in 'builtins.flakeRefToString'"); + attrs.emplace(state.symbols[attr.name], std::string(s)); } else { state .error( @@ -154,7 +166,7 @@ static void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value ** } } auto flakeRef = FlakeRef::fromAttrs(state.fetchSettings, attrs); - v.mkString(flakeRef.to_string(), state.mem); + v.mkString(flakeRef.to_string(), context, state.mem); } nix::PrimOp flakeRefToString({ diff --git a/tests/functional/flakes/get-flake.sh b/tests/functional/flakes/get-flake.sh new file mode 100644 index 00000000000..4fae472079d --- /dev/null +++ b/tests/functional/flakes/get-flake.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +source ./common.sh + +createFlake1 + +mkdir -p "$flake1Dir/subflake" +cat > "$flake1Dir/subflake/flake.nix" <