Skip to content

Commit 1048855

Browse files
committed
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| ];
1 parent ec7dc56 commit 1048855

File tree

10 files changed

+123
-23
lines changed

10 files changed

+123
-23
lines changed

src/libexpr/eval.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "profiles.hh"
1515
#include "print.hh"
1616
#include "filtering-source-accessor.hh"
17+
#include "forwarding-source-accessor.hh"
1718
#include "memory-source-accessor.hh"
1819
#include "gc-small-vector.hh"
1920
#include "url.hh"
@@ -180,6 +181,34 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
180181
}
181182
}
182183

184+
struct PathDisplaySourceAccessor : ForwardingSourceAccessor
185+
{
186+
ref<EvalState::StorePathAccessors> storePathAccessors;
187+
188+
PathDisplaySourceAccessor(
189+
ref<SourceAccessor> next,
190+
ref<EvalState::StorePathAccessors> storePathAccessors)
191+
: ForwardingSourceAccessor(next)
192+
, storePathAccessors(storePathAccessors)
193+
{
194+
}
195+
196+
std::string showPath(const CanonPath & path) override
197+
{
198+
/* Find the accessor that produced `path`, if any, and use it
199+
to render a more informative path
200+
(e.g. `«github:foo/bar»/flake.nix` rather than
201+
`/nix/store/hash.../flake.nix`). */
202+
auto ub = storePathAccessors->upper_bound(path);
203+
if (ub != storePathAccessors->begin())
204+
ub--;
205+
if (ub != storePathAccessors->end() && path.isWithin(ub->first))
206+
return ub->second->showPath(path.removePrefix(ub->first));
207+
else
208+
return next->showPath(path);
209+
}
210+
};
211+
183212
static constexpr size_t BASE_ENV_SIZE = 128;
184213

185214
EvalState::EvalState(
@@ -245,6 +274,7 @@ EvalState::EvalState(
245274
}
246275
, repair(NoRepair)
247276
, emptyBindings(0)
277+
, storePathAccessors(make_ref<StorePathAccessors>())
248278
, rootFS(
249279
({
250280
/* In pure eval mode, we provide a filesystem that only
@@ -270,6 +300,8 @@ EvalState::EvalState(
270300
: makeUnionSourceAccessor({accessor, storeFS});
271301
}
272302

303+
accessor = make_ref<PathDisplaySourceAccessor>(accessor, storePathAccessors);
304+
273305
/* Apply access control if needed. */
274306
if (settings.restrictEval || settings.pureEval)
275307
accessor = AllowListSourceAccessor::create(accessor, {},

src/libexpr/eval.hh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,16 @@ public:
245245
/** `"unknown"` */
246246
Value vStringUnknown;
247247

248+
using StorePathAccessors = std::map<CanonPath, ref<SourceAccessor>>;
249+
250+
/**
251+
* A map back to the original `SourceAccessor`s used to produce
252+
* store paths. We keep track of this to produce error messages
253+
* that refer to the original flakerefs.
254+
* FIXME: use Sync.
255+
*/
256+
ref<StorePathAccessors> storePathAccessors;
257+
248258
/**
249259
* The accessor for the root filesystem.
250260
*/

src/libexpr/primops/fetchMercurial.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
6464
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
6565
auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
6666

67-
auto [storePath, input2] = input.fetchToStore(state.store);
67+
auto [storePath, accessor, input2] = input.fetchToStore(state.store);
6868

6969
auto attrs2 = state.buildBindings(8);
7070
state.mkStorePathString(storePath, attrs2.alloc(state.sOutPath));

src/libexpr/primops/fetchTree.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,12 @@ static void fetchTree(
204204
throw Error("input '%s' is not allowed to use the '__final' attribute", input.to_string());
205205
}
206206

207-
auto [storePath, input2] = input.fetchToStore(state.store);
207+
auto [storePath, accessor, input2] = input.fetchToStore(state.store);
208208

209209
state.allowPath(storePath);
210210

211+
state.storePathAccessors->insert_or_assign(CanonPath(state.store->printStorePath(storePath)), accessor);
212+
211213
emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false);
212214
}
213215

src/libfetchers/fetchers.cc

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -187,34 +187,30 @@ bool Input::contains(const Input & other) const
187187
}
188188

189189
// FIXME: remove
190-
std::pair<StorePath, Input> Input::fetchToStore(ref<Store> store) const
190+
std::tuple<StorePath, ref<SourceAccessor>, Input> Input::fetchToStore(ref<Store> store) const
191191
{
192192
if (!scheme)
193193
throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs()));
194194

195-
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
196-
try {
197-
auto [accessor, result] = getAccessorUnchecked(store);
198-
199-
auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, result.getName());
195+
try {
196+
auto [accessor, result] = getAccessorUnchecked(store);
200197

201-
auto narHash = store->queryPathInfo(storePath)->narHash;
202-
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
198+
auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, result.getName());
203199

204-
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
200+
auto narHash = store->queryPathInfo(storePath)->narHash;
201+
result.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
205202

206-
assert(result.isFinal());
203+
result.attrs.insert_or_assign("__final", Explicit<bool>(true));
207204

208-
checkLocks(*this, result);
205+
assert(result.isFinal());
209206

210-
return {storePath, result};
211-
} catch (Error & e) {
212-
e.addTrace({}, "while fetching the input '%s'", to_string());
213-
throw;
214-
}
215-
}();
207+
checkLocks(*this, result);
216208

217-
return {std::move(storePath), input};
209+
return {std::move(storePath), accessor, result};
210+
} catch (Error & e) {
211+
e.addTrace({}, "while fetching the input '%s'", to_string());
212+
throw;
213+
}
218214
}
219215

220216
void Input::checkLocks(Input specified, Input & result)

src/libfetchers/fetchers.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public:
121121
* Fetch the entire input into the Nix store, returning the
122122
* location in the Nix store and the locked input.
123123
*/
124-
std::pair<StorePath, Input> fetchToStore(ref<Store> store) const;
124+
std::tuple<StorePath, ref<SourceAccessor>, Input> fetchToStore(ref<Store> store) const;
125125

126126
/**
127127
* Check the locking attributes in `result` against

src/libflake/flake/flake.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ static StorePath copyInputToStore(
9292

9393
state.allowPath(storePath);
9494

95+
state.storePathAccessors->insert_or_assign(CanonPath(state.store->printStorePath(storePath)), accessor);
96+
9597
auto narHash = state.store->queryPathInfo(storePath)->narHash;
9698
input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
9799

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#pragma once
2+
3+
#include "source-accessor.hh"
4+
5+
namespace nix {
6+
7+
/**
8+
* A source accessor that just forwards every operation to another
9+
* accessor. This is not useful in itself but can be used as a
10+
* superclass for accessors that do change some operations.
11+
*/
12+
struct ForwardingSourceAccessor : SourceAccessor
13+
{
14+
ref<SourceAccessor> next;
15+
16+
ForwardingSourceAccessor(ref<SourceAccessor> next)
17+
: next(next)
18+
{
19+
}
20+
21+
std::string readFile(const CanonPath & path) override
22+
{
23+
return next->readFile(path);
24+
}
25+
26+
void readFile(const CanonPath & path, Sink & sink, std::function<void(uint64_t)> sizeCallback) override
27+
{
28+
next->readFile(path, sink, sizeCallback);
29+
}
30+
31+
std::optional<Stat> maybeLstat(const CanonPath & path) override
32+
{
33+
return next->maybeLstat(path);
34+
}
35+
36+
DirEntries readDirectory(const CanonPath & path) override
37+
{
38+
return next->readDirectory(path);
39+
}
40+
41+
std::string readLink(const CanonPath & path) override
42+
{
43+
return next->readLink(path);
44+
}
45+
46+
std::string showPath(const CanonPath & path) override
47+
{
48+
return next->showPath(path);
49+
}
50+
51+
std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path) override
52+
{
53+
return next->getPhysicalPath(path);
54+
}
55+
};
56+
57+
}

src/libutil/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ headers = [config_h] + files(
213213
'file-system.hh',
214214
'finally.hh',
215215
'fmt.hh',
216+
'forwarding-source-accessor.hh',
216217
'fs-sink.hh',
217218
'git.hh',
218219
'hash.hh',

src/nix/flake.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,9 +1093,9 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
10931093
auto storePath =
10941094
dryRun
10951095
? (*inputNode)->lockedRef.input.computeStorePath(*store)
1096-
: (*inputNode)->lockedRef.input.fetchToStore(store).first;
1096+
: std::get<0>((*inputNode)->lockedRef.input.fetchToStore(store));
10971097
if (json) {
1098-
auto& jsonObj3 = jsonObj2[inputName];
1098+
auto & jsonObj3 = jsonObj2[inputName];
10991099
jsonObj3["path"] = store->printStorePath(storePath);
11001100
sources.insert(std::move(storePath));
11011101
jsonObj3["inputs"] = traverse(**inputNode);

0 commit comments

Comments
 (0)