1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-28 13:41:15 +02:00

Make rootFS's showPath() render the paths from the original accessors

This makes paths in error messages behave similar to lazy-trees,
e.g. instead of store paths like

       error: attribute 'foobar' missing
       at /nix/store/ddzfiipzqlrh3gnprmqbadnsnrxsmc9i-source/machine/configuration.nix:209:7:
          208|
          209|       pkgs.foobar
             |       ^
          210|     ];

you now get

       error: attribute 'foobar' missing
       at /home/eelco/Misc/eelco-configurations/machine/configuration.nix:209:7:
          208|
          209|       pkgs.foobar
             |       ^
          210|     ];
This commit is contained in:
Eelco Dolstra 2025-02-20 01:09:49 +01:00
parent 5506428e67
commit b28bc7ae64
10 changed files with 122 additions and 22 deletions

View file

@ -14,6 +14,7 @@
#include "profiles.hh" #include "profiles.hh"
#include "print.hh" #include "print.hh"
#include "filtering-source-accessor.hh" #include "filtering-source-accessor.hh"
#include "forwarding-source-accessor.hh"
#include "memory-source-accessor.hh" #include "memory-source-accessor.hh"
#include "gc-small-vector.hh" #include "gc-small-vector.hh"
#include "url.hh" #include "url.hh"
@ -180,6 +181,34 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
} }
} }
struct PathDisplaySourceAccessor : ForwardingSourceAccessor
{
ref<EvalState::StorePathAccessors> storePathAccessors;
PathDisplaySourceAccessor(
ref<SourceAccessor> next,
ref<EvalState::StorePathAccessors> 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; static constexpr size_t BASE_ENV_SIZE = 128;
EvalState::EvalState( EvalState::EvalState(
@ -245,6 +274,7 @@ EvalState::EvalState(
} }
, repair(NoRepair) , repair(NoRepair)
, emptyBindings(0) , emptyBindings(0)
, storePathAccessors(make_ref<StorePathAccessors>())
, rootFS( , rootFS(
({ ({
/* In pure eval mode, we provide a filesystem that only /* In pure eval mode, we provide a filesystem that only
@ -270,6 +300,8 @@ EvalState::EvalState(
: makeUnionSourceAccessor({accessor, storeFS}); : makeUnionSourceAccessor({accessor, storeFS});
} }
accessor = make_ref<PathDisplaySourceAccessor>(accessor, storePathAccessors);
/* Apply access control if needed. */ /* Apply access control if needed. */
if (settings.restrictEval || settings.pureEval) if (settings.restrictEval || settings.pureEval)
accessor = AllowListSourceAccessor::create(accessor, {}, accessor = AllowListSourceAccessor::create(accessor, {},

View file

@ -262,6 +262,16 @@ public:
/** `"unknown"` */ /** `"unknown"` */
Value vStringUnknown; Value vStringUnknown;
using StorePathAccessors = std::map<CanonPath, ref<SourceAccessor>>;
/**
* 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> storePathAccessors;
/** /**
* The accessor for the root filesystem. * The accessor for the root filesystem.
*/ */

View file

@ -64,7 +64,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
if (rev) attrs.insert_or_assign("rev", rev->gitRev()); if (rev) attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); 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); auto attrs2 = state.buildBindings(8);
state.mkStorePathString(storePath, attrs2.alloc(state.sOutPath)); state.mkStorePathString(storePath, attrs2.alloc(state.sOutPath));

View file

@ -200,10 +200,12 @@ static void fetchTree(
throw Error("input '%s' is not allowed to use the '__final' attribute", input.to_string()); 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.allowPath(storePath);
state.storePathAccessors->insert_or_assign(CanonPath(state.store->printStorePath(storePath)), accessor);
emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false); emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false);
} }

View file

@ -187,12 +187,11 @@ bool Input::contains(const Input & other) const
} }
// FIXME: remove // FIXME: remove
std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const std::tuple<StorePath, ref<SourceAccessor>, Input> Input::fetchToStore(ref<Store> store) const
{ {
if (!scheme) if (!scheme)
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
try { try {
auto [accessor, result] = getAccessorUnchecked(store); auto [accessor, result] = getAccessorUnchecked(store);
@ -207,14 +206,11 @@ std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
checkLocks(*this, result); checkLocks(*this, result);
return {storePath, result}; return {std::move(storePath), accessor, result};
} catch (Error & e) { } catch (Error & e) {
e.addTrace({}, "while fetching the input '%s'", to_string()); e.addTrace({}, "while fetching the input '%s'", to_string());
throw; throw;
} }
}();
return {std::move(storePath), input};
} }
void Input::checkLocks(Input specified, Input & result) void Input::checkLocks(Input specified, Input & result)

View file

@ -121,7 +121,7 @@ public:
* Fetch the entire input into the Nix store, returning the * Fetch the entire input into the Nix store, returning the
* location in the Nix store and the locked input. * location in the Nix store and the locked input.
*/ */
std::pair<StorePath, Input> fetchToStore(ref<Store> store) const; std::tuple<StorePath, ref<SourceAccessor>, Input> fetchToStore(ref<Store> store) const;
/** /**
* Check the locking attributes in `result` against * Check the locking attributes in `result` against

View file

@ -92,6 +92,8 @@ static StorePath copyInputToStore(
state.allowPath(storePath); state.allowPath(storePath);
state.storePathAccessors->insert_or_assign(CanonPath(state.store->printStorePath(storePath)), accessor);
auto narHash = state.store->queryPathInfo(storePath)->narHash; auto narHash = state.store->queryPathInfo(storePath)->narHash;
input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));

View file

@ -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<SourceAccessor> next;
ForwardingSourceAccessor(ref<SourceAccessor> next)
: next(next)
{
}
std::string readFile(const CanonPath & path) override
{
return next->readFile(path);
}
void readFile(const CanonPath & path, Sink & sink, std::function<void(uint64_t)> sizeCallback) override
{
next->readFile(path, sink, sizeCallback);
}
std::optional<Stat> 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<std::filesystem::path> getPhysicalPath(const CanonPath & path) override
{
return next->getPhysicalPath(path);
}
};
}

View file

@ -215,6 +215,7 @@ headers = [config_h] + files(
'file-system.hh', 'file-system.hh',
'finally.hh', 'finally.hh',
'fmt.hh', 'fmt.hh',
'forwarding-source-accessor.hh',
'fs-sink.hh', 'fs-sink.hh',
'git.hh', 'git.hh',
'hash.hh', 'hash.hh',

View file

@ -1095,7 +1095,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
storePath = storePath =
dryRun dryRun
? (*inputNode)->lockedRef.input.computeStorePath(*store) ? (*inputNode)->lockedRef.input.computeStorePath(*store)
: (*inputNode)->lockedRef.input.fetchToStore(store).first; : std::get<0>((*inputNode)->lockedRef.input.fetchToStore(store));
sources.insert(*storePath); sources.insert(*storePath);
} }
if (json) { if (json) {