mirror of
https://github.com/NixOS/nix
synced 2025-06-28 09:31:16 +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:
parent
5506428e67
commit
b28bc7ae64
10 changed files with 122 additions and 22 deletions
|
@ -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, {},
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -187,34 +187,30 @@ 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);
|
|
||||||
|
|
||||||
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;
|
auto narHash = store->queryPathInfo(storePath)->narHash;
|
||||||
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
|
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
|
||||||
|
|
||||||
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
|
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
|
||||||
|
|
||||||
assert(result.isFinal());
|
assert(result.isFinal());
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
57
src/libutil/forwarding-source-accessor.hh
Normal file
57
src/libutil/forwarding-source-accessor.hh
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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',
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue