diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 4e15175ac..fcfee2d29 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -14,6 +14,7 @@ #include "profiles.hh" #include "print.hh" #include "filtering-source-accessor.hh" +#include "forwarding-source-accessor.hh" #include "memory-source-accessor.hh" #include "gc-small-vector.hh" #include "url.hh" @@ -180,6 +181,34 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } } +struct PathDisplaySourceAccessor : ForwardingSourceAccessor +{ + ref storePathAccessors; + + PathDisplaySourceAccessor( + ref next, + ref storePathAccessors) + : ForwardingSourceAccessor(next) + , storePathAccessors(storePathAccessors) + { + } + + std::string showPath(const CanonPath & path) override + { + /* Find the accessor that produced `path`, if any, and use it + to render a more informative path + (e.g. `«github:foo/bar»/flake.nix` rather than + `/nix/store/hash.../flake.nix`). */ + auto ub = storePathAccessors->upper_bound(path); + if (ub != storePathAccessors->begin()) + ub--; + if (ub != storePathAccessors->end() && path.isWithin(ub->first)) + return ub->second->showPath(path.removePrefix(ub->first)); + else + return next->showPath(path); + } +}; + static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( @@ -245,6 +274,7 @@ EvalState::EvalState( } , repair(NoRepair) , emptyBindings(0) + , storePathAccessors(make_ref()) , rootFS( ({ /* In pure eval mode, we provide a filesystem that only @@ -270,6 +300,8 @@ EvalState::EvalState( : makeUnionSourceAccessor({accessor, storeFS}); } + accessor = make_ref(accessor, storePathAccessors); + /* Apply access control if needed. */ if (settings.restrictEval || settings.pureEval) accessor = AllowListSourceAccessor::create(accessor, {}, diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index eb6f667a2..3797c40a4 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -262,6 +262,16 @@ public: /** `"unknown"` */ Value vStringUnknown; + using StorePathAccessors = std::map>; + + /** + * A map back to the original `SourceAccessor`s used to produce + * store paths. We keep track of this to produce error messages + * that refer to the original flakerefs. + * FIXME: use Sync. + */ + ref storePathAccessors; + /** * The accessor for the root filesystem. */ diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 64e3abf2d..96800d9ef 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -64,7 +64,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a if (rev) attrs.insert_or_assign("rev", rev->gitRev()); auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); - auto [storePath, input2] = input.fetchToStore(state.store); + auto [storePath, accessor, input2] = input.fetchToStore(state.store); auto attrs2 = state.buildBindings(8); state.mkStorePathString(storePath, attrs2.alloc(state.sOutPath)); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 0c82c82bf..8bbc435e4 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -200,10 +200,12 @@ static void fetchTree( throw Error("input '%s' is not allowed to use the '__final' attribute", input.to_string()); } - auto [storePath, input2] = input.fetchToStore(state.store); + auto [storePath, accessor, input2] = input.fetchToStore(state.store); state.allowPath(storePath); + state.storePathAccessors->insert_or_assign(CanonPath(state.store->printStorePath(storePath)), accessor); + emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false); } diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index de1885db9..67728501e 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -187,34 +187,30 @@ bool Input::contains(const Input & other) const } // FIXME: remove -std::pair Input::fetchToStore(ref store) const +std::tuple, Input> Input::fetchToStore(ref store) const { if (!scheme) throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); - auto [storePath, input] = [&]() -> std::pair { - try { - auto [accessor, result] = getAccessorUnchecked(store); + try { + auto [accessor, result] = getAccessorUnchecked(store); - auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, result.getName()); + auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, result.getName()); - auto narHash = store->queryPathInfo(storePath)->narHash; - result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); + auto narHash = store->queryPathInfo(storePath)->narHash; + result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); - result.attrs.insert_or_assign("__final", Explicit(true)); + result.attrs.insert_or_assign("__final", Explicit(true)); - assert(result.isFinal()); + assert(result.isFinal()); - checkLocks(*this, result); + checkLocks(*this, result); - return {storePath, result}; - } catch (Error & e) { - e.addTrace({}, "while fetching the input '%s'", to_string()); - throw; - } - }(); - - return {std::move(storePath), input}; + return {std::move(storePath), accessor, result}; + } catch (Error & e) { + e.addTrace({}, "while fetching the input '%s'", to_string()); + throw; + } } void Input::checkLocks(Input specified, Input & result) diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 01354a6e3..798d60177 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -121,7 +121,7 @@ public: * Fetch the entire input into the Nix store, returning the * location in the Nix store and the locked input. */ - std::pair fetchToStore(ref store) const; + std::tuple, Input> fetchToStore(ref store) const; /** * Check the locking attributes in `result` against diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index b678d5b64..a14b55c6a 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -92,6 +92,8 @@ static StorePath copyInputToStore( state.allowPath(storePath); + state.storePathAccessors->insert_or_assign(CanonPath(state.store->printStorePath(storePath)), accessor); + auto narHash = state.store->queryPathInfo(storePath)->narHash; input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); diff --git a/src/libutil/forwarding-source-accessor.hh b/src/libutil/forwarding-source-accessor.hh new file mode 100644 index 000000000..bdba2addc --- /dev/null +++ b/src/libutil/forwarding-source-accessor.hh @@ -0,0 +1,57 @@ +#pragma once + +#include "source-accessor.hh" + +namespace nix { + +/** + * A source accessor that just forwards every operation to another + * accessor. This is not useful in itself but can be used as a + * superclass for accessors that do change some operations. + */ +struct ForwardingSourceAccessor : SourceAccessor +{ + ref next; + + ForwardingSourceAccessor(ref next) + : next(next) + { + } + + std::string readFile(const CanonPath & path) override + { + return next->readFile(path); + } + + void readFile(const CanonPath & path, Sink & sink, std::function sizeCallback) override + { + next->readFile(path, sink, sizeCallback); + } + + std::optional maybeLstat(const CanonPath & path) override + { + return next->maybeLstat(path); + } + + DirEntries readDirectory(const CanonPath & path) override + { + return next->readDirectory(path); + } + + std::string readLink(const CanonPath & path) override + { + return next->readLink(path); + } + + std::string showPath(const CanonPath & path) override + { + return next->showPath(path); + } + + std::optional getPhysicalPath(const CanonPath & path) override + { + return next->getPhysicalPath(path); + } +}; + +} diff --git a/src/libutil/meson.build b/src/libutil/meson.build index ab8f8f4db..b2bc0b4ec 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -215,6 +215,7 @@ headers = [config_h] + files( 'file-system.hh', 'finally.hh', 'fmt.hh', + 'forwarding-source-accessor.hh', 'fs-sink.hh', 'git.hh', 'hash.hh', diff --git a/src/nix/flake.cc b/src/nix/flake.cc index cbd412547..9ffe65b06 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1095,7 +1095,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun storePath = dryRun ? (*inputNode)->lockedRef.input.computeStorePath(*store) - : (*inputNode)->lockedRef.input.fetchToStore(store).first; + : std::get<0>((*inputNode)->lockedRef.input.fetchToStore(store)); sources.insert(*storePath); } if (json) {