diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 18abc92d7..8020b3b31 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1978,6 +1978,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * const * lists, co v.mkList(list); } +// FIXME limit recursion Value * resolveOutPath(EvalState & state, Value * v, const PosIdx pos) { state.forceValue(*v, pos); @@ -2335,6 +2336,8 @@ BackedStringView EvalState::coerceToString( v.payload.path.path : copyToStore ? store->printStorePath(copyPathToStore(context, v.path())) + : v.path().accessor->toStringReturnsStorePath() + ? store->printStorePath(copyPathToStore(context, SourcePath(v.path().accessor, CanonPath::root))) + v.path().path.absOrEmpty() : std::string(v.path().path.abs()); } @@ -2447,10 +2450,14 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext if (v.type() == nPath) return v.path(); - /* Similarly, handle __toString where the result may be a path + /* Similarly, handle outPath and __toString where the result may be a path value. */ if (v.type() == nAttrs) { - auto i = v.attrs()->find(sToString); + auto i = v.attrs()->find(sOutPath); + if (i != v.attrs()->end()) { + return coerceToPath(pos, *i->value, context, errorCtx); + } + i = v.attrs()->find(sToString); if (i != v.attrs()->end()) { Value v1; callFunction(*i->value, v, v1, pos); @@ -3115,6 +3122,8 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pat store, fetchSettings, EvalSettings::resolvePseudoUrl(value)); + // Traditional search path lookups use the absolute path space for + // historical consistency. auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy); res.emplace(rootPath(CanonPath(store->toRealPath(storePath)))); } catch (Error & e) { diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 4bfed40db..dfe8df00a 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -136,7 +136,10 @@ struct ExprPath : Expr ExprPath(SourcePath && path) : path(path) { - v.mkPath(&*path.accessor, strdup(path.path.abs().c_str())); + v.mkPath( + &*path.accessor, + // TODO: GC_STRDUP + strdup(path.path.abs().c_str())); } Value * maybeThunk(EvalState & state, Env & env) override; COMMON_METHODS diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 61bc3b985..62b612187 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -92,7 +92,7 @@ struct FetchTreeParams { bool emptyRevFallback = false; bool allowNameArgument = false; bool isFetchGit = false; - bool returnPath = true; // whether to return a SourcePath or a StorePath + bool returnPath = true; // whether to return a SourcePath instead of a StorePath }; static void fetchTree( @@ -212,18 +212,31 @@ static void fetchTree( state.checkURI(input.toURLString()); - auto [storePath, input2] = input.fetchToStore(state.store); + if (params.returnPath) { + // Clang16+: change to `auto [accessor, input2] =` + auto pair = input.getAccessor(state.store); + auto & accessor = pair.first; + auto & input2 = pair.second; - state.allowPath(storePath); + emitTreeAttrs(state, input2, v, + [&](Value & vOutPath) { + state.registerAccessor(accessor); + vOutPath.mkPath(SourcePath { accessor, CanonPath::root }); + }, + params.emptyRevFallback, false); + } else { + auto pair = input.fetchToStore(state.store); + auto & storePath = pair.first; + auto & input2 = pair.second; - emitTreeAttrs(state, input2, v, - [&](Value & vOutPath) { - if (params.returnPath) - vOutPath.mkPath(state.rootPath(state.store->toRealPath(storePath))); - else + state.allowPath(storePath); + + emitTreeAttrs(state, input2, v, + [&](Value & vOutPath) { state.mkStorePathString(storePath, vOutPath); - }, - params.emptyRevFallback, false); + }, + params.emptyRevFallback, false); + } } static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 2b1a485d5..3df84f635 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -195,4 +195,9 @@ ref makeFSSourceAccessor(std::filesystem::path root) { return make_ref(std::move(root)); } + +bool PosixSourceAccessor::toStringReturnsStorePath() const { + return false; +} + } diff --git a/src/libutil/posix-source-accessor.hh b/src/libutil/posix-source-accessor.hh index 40f60bb54..f80425276 100644 --- a/src/libutil/posix-source-accessor.hh +++ b/src/libutil/posix-source-accessor.hh @@ -57,6 +57,8 @@ struct PosixSourceAccessor : virtual SourceAccessor */ static SourcePath createAtRoot(const std::filesystem::path & path); + virtual bool toStringReturnsStorePath() const override; + private: /** diff --git a/src/libutil/source-accessor.cc b/src/libutil/source-accessor.cc index e797951c7..131e10e56 100644 --- a/src/libutil/source-accessor.cc +++ b/src/libutil/source-accessor.cc @@ -105,4 +105,8 @@ CanonPath SourceAccessor::resolveSymlinks( return res; } +bool SourceAccessor::toStringReturnsStorePath() const { + return true; +} + } diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index b16960d4a..79cec5e99 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -161,6 +161,15 @@ struct SourceAccessor : std::enable_shared_from_this virtual std::string showPath(const CanonPath & path); + /** + * System paths: `toString /foo/bar = "/foo/bar"` + * Virtual paths: fetched to the store + * + * In both cases, the returned string functionally identifies the path, + * and can still be read. + */ + virtual bool toStringReturnsStorePath() const; + /** * Resolve any symlinks in `path` according to the given * resolution mode.